@@ -683,14 +683,27 @@ func (s *state) stmt(n *Node) {
683
683
684
684
// Evaluate RHS.
685
685
rhs := n .Right
686
- if rhs != nil && (rhs .Op == OSTRUCTLIT || rhs .Op == OARRAYLIT ) {
687
- // All literals with nonzero fields have already been
688
- // rewritten during walk. Any that remain are just T{}
689
- // or equivalents. Use the zero value.
690
- if ! iszero (rhs ) {
691
- Fatalf ("literal with nonzero value in SSA: %v" , rhs )
686
+ if rhs != nil {
687
+ switch rhs .Op {
688
+ case OSTRUCTLIT , OARRAYLIT :
689
+ // All literals with nonzero fields have already been
690
+ // rewritten during walk. Any that remain are just T{}
691
+ // or equivalents. Use the zero value.
692
+ if ! iszero (rhs ) {
693
+ Fatalf ("literal with nonzero value in SSA: %v" , rhs )
694
+ }
695
+ rhs = nil
696
+ case OAPPEND :
697
+ // If we're writing the result of an append back to the same slice,
698
+ // handle it specially to avoid write barriers on the fast (non-growth) path.
699
+ // If the slice can be SSA'd, it'll be on the stack,
700
+ // so there will be no write barriers,
701
+ // so there's no need to attempt to prevent them.
702
+ if samesafeexpr (n .Left , rhs .List .First ()) && ! s .canSSA (n .Left ) {
703
+ s .append (rhs , true )
704
+ return
705
+ }
692
706
}
693
- rhs = nil
694
707
}
695
708
var r * ssa.Value
696
709
needwb := n .Op == OASWB && rhs != nil
@@ -709,11 +722,11 @@ func (s *state) stmt(n *Node) {
709
722
}
710
723
}
711
724
if rhs != nil && rhs .Op == OAPPEND {
712
- // Yuck! The frontend gets rid of the write barrier, but we need it!
713
- // At least, we need it in the case where growslice is called.
714
- // TODO: Do the write barrier on just the growslice branch.
725
+ // The frontend gets rid of the write barrier to enable the special OAPPEND
726
+ // handling above, but since this is not a special case, we need it.
715
727
// TODO: just add a ptr graying to the end of growslice?
716
- // TODO: check whether we need to do this for ODOTTYPE and ORECV also.
728
+ // TODO: check whether we need to provide special handling and a write barrier
729
+ // for ODOTTYPE and ORECV also.
717
730
// They get similar wb-removal treatment in walk.go:OAS.
718
731
needwb = true
719
732
}
@@ -2079,33 +2092,65 @@ func (s *state) expr(n *Node) *ssa.Value {
2079
2092
return s .newValue1 (ssa .OpGetG , n .Type , s .mem ())
2080
2093
2081
2094
case OAPPEND :
2082
- return s .exprAppend ( n )
2095
+ return s .append ( n , false )
2083
2096
2084
2097
default :
2085
2098
s .Unimplementedf ("unhandled expr %s" , opnames [n .Op ])
2086
2099
return nil
2087
2100
}
2088
2101
}
2089
2102
2090
- // exprAppend converts an OAPPEND node n to an ssa.Value, adds it to s, and returns the Value.
2091
- func (s * state ) exprAppend (n * Node ) * ssa.Value {
2092
- // append(s, e1, e2, e3). Compile like:
2103
+ // append converts an OAPPEND node to SSA.
2104
+ // If inplace is false, it converts the OAPPEND expression n to an ssa.Value,
2105
+ // adds it to s, and returns the Value.
2106
+ // If inplace is true, it writes the result of the OAPPEND expression n
2107
+ // back to the slice being appended to, and returns nil.
2108
+ // inplace MUST be set to false if the slice can be SSA'd.
2109
+ func (s * state ) append (n * Node , inplace bool ) * ssa.Value {
2110
+ // If inplace is false, process as expression "append(s, e1, e2, e3)":
2111
+ //
2093
2112
// ptr, len, cap := s
2094
2113
// newlen := len + 3
2095
- // if newlen > s. cap {
2114
+ // if newlen > cap {
2096
2115
// ptr, len, cap = growslice(s, newlen)
2097
2116
// newlen = len + 3 // recalculate to avoid a spill
2098
2117
// }
2118
+ // // with write barriers, if needed:
2119
+ // *(ptr+len) = e1
2120
+ // *(ptr+len+1) = e2
2121
+ // *(ptr+len+2) = e3
2122
+ // return makeslice(ptr, newlen, cap)
2123
+ //
2124
+ //
2125
+ // If inplace is true, process as statement "s = append(s, e1, e2, e3)":
2126
+ //
2127
+ // a := &s
2128
+ // ptr, len, cap := s
2129
+ // newlen := len + 3
2130
+ // *a.len = newlen // store newlen immediately to avoid a spill
2131
+ // if newlen > cap {
2132
+ // newptr, _, newcap = growslice(ptr, len, cap, newlen)
2133
+ // *a.cap = newcap // write before ptr to avoid a spill
2134
+ // *a.ptr = newptr // with write barrier
2135
+ // }
2136
+ // // with write barriers, if needed:
2099
2137
// *(ptr+len) = e1
2100
2138
// *(ptr+len+1) = e2
2101
2139
// *(ptr+len+2) = e3
2102
- // makeslice(ptr, newlen, cap)
2103
2140
2104
2141
et := n .Type .Elem ()
2105
2142
pt := Ptrto (et )
2106
2143
2107
2144
// Evaluate slice
2108
- slice := s .expr (n .List .First ())
2145
+ sn := n .List .First () // the slice node is the first in the list
2146
+
2147
+ var slice , addr * ssa.Value
2148
+ if inplace {
2149
+ addr = s .addr (sn , false )
2150
+ slice = s .newValue2 (ssa .OpLoad , n .Type , addr , s .mem ())
2151
+ } else {
2152
+ slice = s .expr (sn )
2153
+ }
2109
2154
2110
2155
// Allocate new blocks
2111
2156
grow := s .f .NewBlock (ssa .BlockPlain )
@@ -2117,10 +2162,20 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
2117
2162
l := s .newValue1 (ssa .OpSliceLen , Types [TINT ], slice )
2118
2163
c := s .newValue1 (ssa .OpSliceCap , Types [TINT ], slice )
2119
2164
nl := s .newValue2 (s .ssaOp (OADD , Types [TINT ]), Types [TINT ], l , s .constInt (Types [TINT ], nargs ))
2165
+
2166
+ if inplace {
2167
+ lenaddr := s .newValue1I (ssa .OpOffPtr , pt , int64 (Array_nel ), addr )
2168
+ s .vars [& memVar ] = s .newValue3I (ssa .OpStore , ssa .TypeMem , s .config .IntSize , lenaddr , nl , s .mem ())
2169
+ }
2170
+
2120
2171
cmp := s .newValue2 (s .ssaOp (OGT , Types [TINT ]), Types [TBOOL ], nl , c )
2121
2172
s .vars [& ptrVar ] = p
2122
- s .vars [& newlenVar ] = nl
2123
- s .vars [& capVar ] = c
2173
+
2174
+ if ! inplace {
2175
+ s .vars [& newlenVar ] = nl
2176
+ s .vars [& capVar ] = c
2177
+ }
2178
+
2124
2179
b := s .endBlock ()
2125
2180
b .Kind = ssa .BlockIf
2126
2181
b .Likely = ssa .BranchUnlikely
@@ -2134,9 +2189,18 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
2134
2189
2135
2190
r := s .rtcall (growslice , true , []* Type {pt , Types [TINT ], Types [TINT ]}, taddr , p , l , c , nl )
2136
2191
2137
- s .vars [& ptrVar ] = r [0 ]
2138
- s .vars [& newlenVar ] = s .newValue2 (s .ssaOp (OADD , Types [TINT ]), Types [TINT ], r [1 ], s .constInt (Types [TINT ], nargs ))
2139
- s .vars [& capVar ] = r [2 ]
2192
+ if inplace {
2193
+ capaddr := s .newValue1I (ssa .OpOffPtr , pt , int64 (Array_cap ), addr )
2194
+ s .vars [& memVar ] = s .newValue3I (ssa .OpStore , ssa .TypeMem , s .config .IntSize , capaddr , r [2 ], s .mem ())
2195
+ s .insertWBstore (pt , addr , r [0 ], n .Lineno , 0 )
2196
+ // load the value we just stored to avoid having to spill it
2197
+ s .vars [& ptrVar ] = s .newValue2 (ssa .OpLoad , pt , addr , s .mem ())
2198
+ } else {
2199
+ s .vars [& ptrVar ] = r [0 ]
2200
+ s .vars [& newlenVar ] = s .newValue2 (s .ssaOp (OADD , Types [TINT ]), Types [TINT ], r [1 ], s .constInt (Types [TINT ], nargs ))
2201
+ s .vars [& capVar ] = r [2 ]
2202
+ }
2203
+
2140
2204
b = s .endBlock ()
2141
2205
b .AddEdgeTo (assign )
2142
2206
@@ -2156,9 +2220,11 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
2156
2220
}
2157
2221
}
2158
2222
2159
- p = s .variable (& ptrVar , pt ) // generates phi for ptr
2160
- nl = s .variable (& newlenVar , Types [TINT ]) // generates phi for nl
2161
- c = s .variable (& capVar , Types [TINT ]) // generates phi for cap
2223
+ p = s .variable (& ptrVar , pt ) // generates phi for ptr
2224
+ if ! inplace {
2225
+ nl = s .variable (& newlenVar , Types [TINT ]) // generates phi for nl
2226
+ c = s .variable (& capVar , Types [TINT ]) // generates phi for cap
2227
+ }
2162
2228
p2 := s .newValue2 (ssa .OpPtrIndex , pt , p , l )
2163
2229
// TODO: just one write barrier call for all of these writes?
2164
2230
// TODO: maybe just one writeBarrier.enabled check?
@@ -2179,10 +2245,13 @@ func (s *state) exprAppend(n *Node) *ssa.Value {
2179
2245
}
2180
2246
}
2181
2247
2182
- // make result
2183
2248
delete (s .vars , & ptrVar )
2249
+ if inplace {
2250
+ return nil
2251
+ }
2184
2252
delete (s .vars , & newlenVar )
2185
2253
delete (s .vars , & capVar )
2254
+ // make result
2186
2255
return s .newValue3 (ssa .OpSliceMake , n .Type , p , nl , c )
2187
2256
}
2188
2257
0 commit comments