@@ -654,9 +654,67 @@ func (e *EscState) esclist(l Nodes, parent *Node) {
654
654
}
655
655
}
656
656
657
+ func (e * EscState ) isSliceSelfAssign (dst , src * Node ) bool {
658
+ // Detect the following special case.
659
+ //
660
+ // func (b *Buffer) Foo() {
661
+ // n, m := ...
662
+ // b.buf = b.buf[n:m]
663
+ // }
664
+ //
665
+ // This assignment is a no-op for escape analysis,
666
+ // it does not store any new pointers into b that were not already there.
667
+ // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
668
+ // Here we assume that the statement will not contain calls,
669
+ // that is, that order will move any calls to init.
670
+ // Otherwise base ONAME value could change between the moments
671
+ // when we evaluate it for dst and for src.
672
+
673
+ // dst is ONAME dereference.
674
+ if dst .Op != OIND && dst .Op != ODOTPTR || dst .Left .Op != ONAME {
675
+ return false
676
+ }
677
+ // src is a slice operation.
678
+ switch src .Op {
679
+ case OSLICE , OSLICE3 , OSLICESTR :
680
+ // OK.
681
+ case OSLICEARR , OSLICE3ARR :
682
+ // Since arrays are embedded into containing object,
683
+ // slice of non-pointer array will introduce a new pointer into b that was not already there
684
+ // (pointer to b itself). After such assignment, if b contents escape,
685
+ // b escapes as well. If we ignore such OSLICEARR, we will conclude
686
+ // that b does not escape when b contents do.
687
+ //
688
+ // Pointer to an array is OK since it's not stored inside b directly.
689
+ // For slicing an array (not pointer to array), there is an implicit OADDR.
690
+ // We check that to determine non-pointer array slicing.
691
+ if src .Left .Op == OADDR {
692
+ return false
693
+ }
694
+ default :
695
+ return false
696
+ }
697
+ // slice is applied to ONAME dereference.
698
+ if src .Left .Op != OIND && src .Left .Op != ODOTPTR || src .Left .Left .Op != ONAME {
699
+ return false
700
+ }
701
+ // dst and src reference the same base ONAME.
702
+ return dst .Left == src .Left .Left
703
+ }
704
+
657
705
// isSelfAssign reports whether assignment from src to dst can
658
706
// be ignored by the escape analysis as it's effectively a self-assignment.
659
707
func (e * EscState ) isSelfAssign (dst , src * Node ) bool {
708
+ // Detect trivial assignments that assign back to the same object.
709
+ //
710
+ // It covers these cases:
711
+ // val.x = val.y
712
+ // val.x[i] = val.y[j]
713
+ // val.x1.x2 = val.x1.y2
714
+ // ... etc
715
+ //
716
+ // These assignments do not change assigned object lifetime.
717
+
660
718
if dst == nil || src == nil || dst .Op != src .Op {
661
719
return false
662
720
}
@@ -830,48 +888,15 @@ opSwitch:
830
888
}
831
889
}
832
890
833
- // Filter out the following special case.
834
- //
835
- // func (b *Buffer) Foo() {
836
- // n, m := ...
837
- // b.buf = b.buf[n:m]
838
- // }
839
- //
840
- // This assignment is a no-op for escape analysis,
841
- // it does not store any new pointers into b that were not already there.
842
- // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
843
891
case OAS , OASOP :
844
- if (n .Left .Op == OIND || n .Left .Op == ODOTPTR ) && n .Left .Left .Op == ONAME && // dst is ONAME dereference
845
- (n .Right .Op == OSLICE || n .Right .Op == OSLICE3 || n .Right .Op == OSLICESTR ) && // src is slice operation
846
- (n .Right .Left .Op == OIND || n .Right .Left .Op == ODOTPTR ) && n .Right .Left .Left .Op == ONAME && // slice is applied to ONAME dereference
847
- n .Left .Left == n .Right .Left .Left { // dst and src reference the same base ONAME
848
-
849
- // Here we also assume that the statement will not contain calls,
850
- // that is, that order will move any calls to init.
851
- // Otherwise base ONAME value could change between the moments
852
- // when we evaluate it for dst and for src.
853
- //
854
- // Note, this optimization does not apply to OSLICEARR,
855
- // because it does introduce a new pointer into b that was not already there
856
- // (pointer to b itself). After such assignment, if b contents escape,
857
- // b escapes as well. If we ignore such OSLICEARR, we will conclude
858
- // that b does not escape when b contents do.
892
+ // Filter out some no-op assignments for escape analysis.
893
+ if e .isSliceSelfAssign (n .Left , n .Right ) {
859
894
if Debug ['m' ] != 0 {
860
895
Warnl (n .Pos , "%v ignoring self-assignment to %S" , e .curfnSym (n ), n .Left )
861
896
}
862
897
863
898
break
864
899
}
865
-
866
- // Also skip trivial assignments that assign back to the same object.
867
- //
868
- // It covers these cases:
869
- // val.x = val.y
870
- // val.x[i] = val.y[j]
871
- // val.x1.x2 = val.x1.y2
872
- // ... etc
873
- //
874
- // These assignments do not change assigned object lifetime.
875
900
if e .isSelfAssign (n .Left , n .Right ) {
876
901
if Debug ['m' ] != 0 {
877
902
Warnl (n .Pos , "%v ignoring self-assignment in %S" , e .curfnSym (n ), n )
0 commit comments