@@ -728,9 +728,13 @@ opswitch:
728
728
if r .Type .Elem ().NotInHeap () {
729
729
yyerror ("%v is go:notinheap; heap allocation disallowed" , r .Type .Elem ())
730
730
}
731
- if r .Isddd () {
731
+ switch {
732
+ case isAppendOfMake (r ):
733
+ // x = append(y, make([]T, y)...)
734
+ r = extendslice (r , init )
735
+ case r .Isddd ():
732
736
r = appendslice (r , init ) // also works for append(slice, string).
733
- } else {
737
+ default :
734
738
r = walkappend (r , init , n )
735
739
}
736
740
n .Right = r
@@ -2910,6 +2914,18 @@ func addstr(n *Node, init *Nodes) *Node {
2910
2914
return r
2911
2915
}
2912
2916
2917
+ func walkAppendArgs (n * Node , init * Nodes ) {
2918
+ walkexprlistsafe (n .List .Slice (), init )
2919
+
2920
+ // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
2921
+ // and n are name or literal, but those may index the slice we're
2922
+ // modifying here. Fix explicitly.
2923
+ ls := n .List .Slice ()
2924
+ for i1 , n1 := range ls {
2925
+ ls [i1 ] = cheapexpr (n1 , init )
2926
+ }
2927
+ }
2928
+
2913
2929
// expand append(l1, l2...) to
2914
2930
// init {
2915
2931
// s := l1
@@ -2925,15 +2941,7 @@ func addstr(n *Node, init *Nodes) *Node {
2925
2941
//
2926
2942
// l2 is allowed to be a string.
2927
2943
func appendslice (n * Node , init * Nodes ) * Node {
2928
- walkexprlistsafe (n .List .Slice (), init )
2929
-
2930
- // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
2931
- // and n are name or literal, but those may index the slice we're
2932
- // modifying here. Fix explicitly.
2933
- ls := n .List .Slice ()
2934
- for i1 , n1 := range ls {
2935
- ls [i1 ] = cheapexpr (n1 , init )
2936
- }
2944
+ walkAppendArgs (n , init )
2937
2945
2938
2946
l1 := n .List .First ()
2939
2947
l2 := n .List .Second ()
@@ -3027,6 +3035,174 @@ func appendslice(n *Node, init *Nodes) *Node {
3027
3035
return s
3028
3036
}
3029
3037
3038
+ // isAppendOfMake reports whether n is of the form append(x , make([]T, y)...).
3039
+ // isAppendOfMake assumes n has already been typechecked.
3040
+ func isAppendOfMake (n * Node ) bool {
3041
+ if Debug ['N' ] != 0 || instrumenting {
3042
+ return false
3043
+ }
3044
+
3045
+ if n .Typecheck () == 0 {
3046
+ Fatalf ("missing typecheck: %+v" , n )
3047
+ }
3048
+
3049
+ if n .Op != OAPPEND || ! n .Isddd () || n .List .Len () != 2 {
3050
+ return false
3051
+ }
3052
+
3053
+ second := n .List .Second ()
3054
+ if second .Op != OMAKESLICE {
3055
+ return false
3056
+ }
3057
+
3058
+ if n .List .Second ().Right != nil {
3059
+ return false
3060
+ }
3061
+
3062
+ // y must be either an integer constant or a variable of type int.
3063
+ // typecheck checks that constant arguments to make are not negative and
3064
+ // fit into an int.
3065
+ // runtime.growslice uses int as type for the newcap argument.
3066
+ // Constraining variables to be type int avoids the need for runtime checks
3067
+ // that e.g. check if an int64 value fits into an int.
3068
+ // TODO(moehrmann): support other integer types that always fit in an int
3069
+ y := second .Left
3070
+ if ! Isconst (y , CTINT ) && y .Type .Etype != TINT {
3071
+ return false
3072
+ }
3073
+
3074
+ return true
3075
+ }
3076
+
3077
+ // extendslice rewrites append(l1, make([]T, l2)...) to
3078
+ // init {
3079
+ // if l2 < 0 {
3080
+ // panicmakeslicelen()
3081
+ // }
3082
+ // s := l1
3083
+ // n := len(s) + l2
3084
+ // // Compare n and s as uint so growslice can panic on overflow of len(s) + l2.
3085
+ // // cap is a positive int and n can become negative when len(s) + l2
3086
+ // // overflows int. Interpreting n when negative as uint makes it larger
3087
+ // // than cap(s). growslice will check the int n arg and panic if n is
3088
+ // // negative. This prevents the overflow from being undetected.
3089
+ // if uint(n) > uint(cap(s)) {
3090
+ // s = growslice(T, s, n)
3091
+ // }
3092
+ // s = s[:n]
3093
+ // lptr := &l1[0]
3094
+ // sptr := &s[0]
3095
+ // if lptr == sptr || !hasPointers(T) {
3096
+ // // growslice did not clear the whole underlying array (or did not get called)
3097
+ // hp := &s[len(l1)]
3098
+ // hn := l2 * sizeof(T)
3099
+ // memclr(hp, hn)
3100
+ // }
3101
+ // }
3102
+ // s
3103
+ func extendslice (n * Node , init * Nodes ) * Node {
3104
+ // isAppendOfMake made sure l2 fits in an int.
3105
+ l2 := conv (n .List .Second ().Left , types .Types [TINT ])
3106
+ l2 = typecheck (l2 , Erv )
3107
+ n .List .SetSecond (l2 ) // walkAppendArgs expects l2 in n.List.Second().
3108
+
3109
+ walkAppendArgs (n , init )
3110
+
3111
+ l1 := n .List .First ()
3112
+ l2 = n .List .Second () // re-read l2, as it may have been updated by walkAppendArgs
3113
+
3114
+ var nodes []* Node
3115
+
3116
+ // if l2 < 0
3117
+ nifneg := nod (OIF , nod (OLT , l2 , nodintconst (0 )), nil )
3118
+ nifneg .SetLikely (false )
3119
+
3120
+ // panicmakeslicelen()
3121
+ nifneg .Nbody .Set1 (mkcall ("panicmakeslicelen" , nil , init ))
3122
+ nodes = append (nodes , nifneg )
3123
+
3124
+ // s := l1
3125
+ s := temp (l1 .Type )
3126
+ nodes = append (nodes , nod (OAS , s , l1 ))
3127
+
3128
+ elemtype := s .Type .Elem ()
3129
+
3130
+ // n := len(s) + l2
3131
+ nn := temp (types .Types [TINT ])
3132
+ nodes = append (nodes , nod (OAS , nn , nod (OADD , nod (OLEN , s , nil ), l2 )))
3133
+
3134
+ // if uint(n) > uint(cap(s))
3135
+ nuint := nod (OCONV , nn , nil )
3136
+ nuint .Type = types .Types [TUINT ]
3137
+ capuint := nod (OCONV , nod (OCAP , s , nil ), nil )
3138
+ capuint .Type = types .Types [TUINT ]
3139
+ nif := nod (OIF , nod (OGT , nuint , capuint ), nil )
3140
+
3141
+ // instantiate growslice(typ *type, old []any, newcap int) []any
3142
+ fn := syslook ("growslice" )
3143
+ fn = substArgTypes (fn , elemtype , elemtype )
3144
+
3145
+ // s = growslice(T, s, n)
3146
+ nif .Nbody .Set1 (nod (OAS , s , mkcall1 (fn , s .Type , & nif .Ninit , typename (elemtype ), s , nn )))
3147
+ nodes = append (nodes , nif )
3148
+
3149
+ // s = s[:n]
3150
+ nt := nod (OSLICE , s , nil )
3151
+ nt .SetSliceBounds (nil , nn , nil )
3152
+ nodes = append (nodes , nod (OAS , s , nt ))
3153
+
3154
+ // lptr := &l1[0]
3155
+ l1ptr := temp (l1 .Type .Elem ().PtrTo ())
3156
+ tmp := nod (OSPTR , l1 , nil )
3157
+ nodes = append (nodes , nod (OAS , l1ptr , tmp ))
3158
+
3159
+ // sptr := &s[0]
3160
+ sptr := temp (elemtype .PtrTo ())
3161
+ tmp = nod (OSPTR , s , nil )
3162
+ nodes = append (nodes , nod (OAS , sptr , tmp ))
3163
+
3164
+ var clr []* Node
3165
+
3166
+ // hp := &s[len(l1)]
3167
+ hp := temp (types .Types [TUNSAFEPTR ])
3168
+
3169
+ tmp = nod (OINDEX , s , nod (OLEN , l1 , nil ))
3170
+ tmp .SetBounded (true )
3171
+ tmp = nod (OADDR , tmp , nil )
3172
+ tmp = nod (OCONVNOP , tmp , nil )
3173
+ tmp .Type = types .Types [TUNSAFEPTR ]
3174
+ clr = append (clr , nod (OAS , hp , tmp ))
3175
+
3176
+ // hn := l2 * sizeof(elem(s))
3177
+ hn := temp (types .Types [TUINTPTR ])
3178
+
3179
+ tmp = nod (OMUL , l2 , nodintconst (elemtype .Width ))
3180
+ tmp = conv (tmp , types .Types [TUINTPTR ])
3181
+ clr = append (clr , nod (OAS , hn , tmp ))
3182
+
3183
+ clrname := "memclrNoHeapPointers"
3184
+ hasPointers := types .Haspointers (elemtype )
3185
+ if hasPointers {
3186
+ clrname = "memclrHasPointers"
3187
+ }
3188
+ clrfn := mkcall (clrname , nil , init , hp , hn )
3189
+ clr = append (clr , clrfn )
3190
+
3191
+ if hasPointers {
3192
+ // if l1ptr == sptr
3193
+ nifclr := nod (OIF , nod (OEQ , l1ptr , sptr ), nil )
3194
+ nifclr .Nbody .Set (clr )
3195
+ nodes = append (nodes , nifclr )
3196
+ } else {
3197
+ nodes = append (nodes , clr ... )
3198
+ }
3199
+
3200
+ typecheckslice (nodes , Etop )
3201
+ walkstmtlist (nodes )
3202
+ init .Append (nodes ... )
3203
+ return s
3204
+ }
3205
+
3030
3206
// Rewrite append(src, x, y, z) so that any side effects in
3031
3207
// x, y, z (including runtime panics) are evaluated in
3032
3208
// initialization statements before the append.
0 commit comments