@@ -580,8 +580,8 @@ func (s *state) stmt(n *Node) {
580
580
581
581
case OAS2DOTTYPE :
582
582
res , resok := s .dottype (n .Rlist .First (), true )
583
- s .assign (n .List .First (), res , needwritebarrier (n .List .First (), n .Rlist .First ()), false , n .Lineno , 0 )
584
- s .assign (n .List .Second (), resok , false , false , n .Lineno , 0 )
583
+ s .assign (n .List .First (), res , needwritebarrier (n .List .First (), n .Rlist .First ()), false , n .Lineno , 0 , false )
584
+ s .assign (n .List .Second (), resok , false , false , n .Lineno , 0 , false )
585
585
return
586
586
587
587
case ODCL :
@@ -700,13 +700,14 @@ func (s *state) stmt(n *Node) {
700
700
}
701
701
}
702
702
var r * ssa.Value
703
+ var isVolatile bool
703
704
needwb := n .Op == OASWB && rhs != nil
704
705
deref := ! canSSAType (t )
705
706
if deref {
706
707
if rhs == nil {
707
708
r = nil // Signal assign to use OpZero.
708
709
} else {
709
- r = s .addr (rhs , false )
710
+ r , isVolatile = s .addr (rhs , false )
710
711
}
711
712
} else {
712
713
if rhs == nil {
@@ -755,7 +756,7 @@ func (s *state) stmt(n *Node) {
755
756
}
756
757
}
757
758
758
- s .assign (n .Left , r , needwb , deref , n .Lineno , skip )
759
+ s .assign (n .Left , r , needwb , deref , n .Lineno , skip , isVolatile )
759
760
760
761
case OIF :
761
762
bThen := s .f .NewBlock (ssa .BlockPlain )
@@ -1438,10 +1439,10 @@ func (s *state) expr(n *Node) *ssa.Value {
1438
1439
if s .canSSA (n ) {
1439
1440
return s .variable (n , n .Type )
1440
1441
}
1441
- addr := s .addr (n , false )
1442
+ addr , _ := s .addr (n , false )
1442
1443
return s .newValue2 (ssa .OpLoad , n .Type , addr , s .mem ())
1443
1444
case OCLOSUREVAR :
1444
- addr := s .addr (n , false )
1445
+ addr , _ := s .addr (n , false )
1445
1446
return s .newValue2 (ssa .OpLoad , n .Type , addr , s .mem ())
1446
1447
case OLITERAL :
1447
1448
switch u := n .Val ().U .(type ) {
@@ -1910,7 +1911,9 @@ func (s *state) expr(n *Node) *ssa.Value {
1910
1911
return s .expr (n .Left )
1911
1912
1912
1913
case OADDR :
1913
- return s .addr (n .Left , n .Bounded )
1914
+ a , _ := s .addr (n .Left , n .Bounded )
1915
+ // Note we know the volatile result is false because you can't write &f() in Go.
1916
+ return a
1914
1917
1915
1918
case OINDREG :
1916
1919
if int (n .Reg ) != Thearch .REGSP {
@@ -1930,7 +1933,7 @@ func (s *state) expr(n *Node) *ssa.Value {
1930
1933
v := s .expr (n .Left )
1931
1934
return s .newValue1I (ssa .OpStructSelect , n .Type , int64 (fieldIdx (n )), v )
1932
1935
}
1933
- p := s .addr (n , false )
1936
+ p , _ := s .addr (n , false )
1934
1937
return s .newValue2 (ssa .OpLoad , n .Type , p , s .mem ())
1935
1938
1936
1939
case ODOTPTR :
@@ -1957,11 +1960,11 @@ func (s *state) expr(n *Node) *ssa.Value {
1957
1960
}
1958
1961
return s .newValue2 (ssa .OpLoad , Types [TUINT8 ], ptr , s .mem ())
1959
1962
case n .Left .Type .IsSlice ():
1960
- p := s .addr (n , false )
1963
+ p , _ := s .addr (n , false )
1961
1964
return s .newValue2 (ssa .OpLoad , n .Left .Type .Elem (), p , s .mem ())
1962
1965
case n .Left .Type .IsArray ():
1963
1966
// TODO: fix when we can SSA arrays of length 1.
1964
- p := s .addr (n , false )
1967
+ p , _ := s .addr (n , false )
1965
1968
return s .newValue2 (ssa .OpLoad , n .Left .Type .Elem (), p , s .mem ())
1966
1969
default :
1967
1970
s .Fatalf ("bad type for index %v" , n .Left .Type )
@@ -2126,7 +2129,7 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
2126
2129
2127
2130
var slice , addr * ssa.Value
2128
2131
if inplace {
2129
- addr = s .addr (sn , false )
2132
+ addr , _ = s .addr (sn , false )
2130
2133
slice = s .newValue2 (ssa .OpLoad , n .Type , addr , s .mem ())
2131
2134
} else {
2132
2135
slice = s .expr (sn )
@@ -2197,15 +2200,21 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
2197
2200
}
2198
2201
2199
2202
// Evaluate args
2200
- args := make ([]* ssa.Value , 0 , nargs )
2201
- store := make ([]bool , 0 , nargs )
2203
+ type argRec struct {
2204
+ // if store is true, we're appending the value v. If false, we're appending the
2205
+ // value at *v. If store==false, isVolatile reports whether the source
2206
+ // is in the outargs section of the stack frame.
2207
+ v * ssa.Value
2208
+ store bool
2209
+ isVolatile bool
2210
+ }
2211
+ args := make ([]argRec , 0 , nargs )
2202
2212
for _ , n := range n .List .Slice ()[1 :] {
2203
2213
if canSSAType (n .Type ) {
2204
- args = append (args , s .expr (n ))
2205
- store = append (store , true )
2214
+ args = append (args , argRec {v : s .expr (n ), store : true })
2206
2215
} else {
2207
- args = append ( args , s .addr (n , false ) )
2208
- store = append (store , false )
2216
+ v , isVolatile := s .addr (n , false )
2217
+ args = append (args , argRec { v : v , isVolatile : isVolatile } )
2209
2218
}
2210
2219
}
2211
2220
@@ -2219,17 +2228,17 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
2219
2228
// TODO: maybe just one writeBarrier.enabled check?
2220
2229
for i , arg := range args {
2221
2230
addr := s .newValue2 (ssa .OpPtrIndex , pt , p2 , s .constInt (Types [TINT ], int64 (i )))
2222
- if store [ i ] {
2231
+ if arg . store {
2223
2232
if haspointers (et ) {
2224
- s .insertWBstore (et , addr , arg , n .Lineno , 0 )
2233
+ s .insertWBstore (et , addr , arg . v , n .Lineno , 0 )
2225
2234
} else {
2226
- s .vars [& memVar ] = s .newValue3I (ssa .OpStore , ssa .TypeMem , et .Size (), addr , arg , s .mem ())
2235
+ s .vars [& memVar ] = s .newValue3I (ssa .OpStore , ssa .TypeMem , et .Size (), addr , arg . v , s .mem ())
2227
2236
}
2228
2237
} else {
2229
2238
if haspointers (et ) {
2230
- s .insertWBmove (et , addr , arg , n .Lineno )
2239
+ s .insertWBmove (et , addr , arg . v , n .Lineno , arg . isVolatile )
2231
2240
} else {
2232
- s .vars [& memVar ] = s .newValue3I (ssa .OpMove , ssa .TypeMem , et .Size (), addr , arg , s .mem ())
2241
+ s .vars [& memVar ] = s .newValue3I (ssa .OpMove , ssa .TypeMem , et .Size (), addr , arg . v , s .mem ())
2233
2242
}
2234
2243
}
2235
2244
}
@@ -2301,9 +2310,10 @@ const (
2301
2310
// Right has already been evaluated to ssa, left has not.
2302
2311
// If deref is true, then we do left = *right instead (and right has already been nil-checked).
2303
2312
// If deref is true and right == nil, just do left = 0.
2313
+ // If deref is true, rightIsVolatile reports whether right points to volatile (clobbered by a call) storage.
2304
2314
// Include a write barrier if wb is true.
2305
2315
// skip indicates assignments (at the top level) that can be avoided.
2306
- func (s * state ) assign (left * Node , right * ssa.Value , wb , deref bool , line int32 , skip skipMask ) {
2316
+ func (s * state ) assign (left * Node , right * ssa.Value , wb , deref bool , line int32 , skip skipMask , rightIsVolatile bool ) {
2307
2317
if left .Op == ONAME && isblank (left ) {
2308
2318
return
2309
2319
}
@@ -2344,7 +2354,7 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
2344
2354
}
2345
2355
2346
2356
// Recursively assign the new value we've made to the base of the dot op.
2347
- s .assign (left .Left , new , false , false , line , 0 )
2357
+ s .assign (left .Left , new , false , false , line , 0 , rightIsVolatile )
2348
2358
// TODO: do we need to update named values here?
2349
2359
return
2350
2360
}
@@ -2354,7 +2364,7 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
2354
2364
return
2355
2365
}
2356
2366
// Left is not ssa-able. Compute its address.
2357
- addr := s .addr (left , false )
2367
+ addr , _ := s .addr (left , false )
2358
2368
if left .Op == ONAME && skip == 0 {
2359
2369
s .vars [& memVar ] = s .newValue1A (ssa .OpVarDef , ssa .TypeMem , left , s .mem ())
2360
2370
}
@@ -2365,7 +2375,7 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
2365
2375
return
2366
2376
}
2367
2377
if wb {
2368
- s .insertWBmove (t , addr , right , line )
2378
+ s .insertWBmove (t , addr , right , line , rightIsVolatile )
2369
2379
return
2370
2380
}
2371
2381
s .vars [& memVar ] = s .newValue3I (ssa .OpMove , ssa .TypeMem , t .Size (), addr , right , s .mem ())
@@ -2684,10 +2694,12 @@ func (s *state) lookupSymbol(n *Node, sym interface{}) interface{} {
2684
2694
}
2685
2695
2686
2696
// addr converts the address of the expression n to SSA, adds it to s and returns the SSA result.
2697
+ // Also returns a bool reporting whether the returned value is "volatile", that is it
2698
+ // points to the outargs section and thus the referent will be clobbered by any call.
2687
2699
// The value that the returned Value represents is guaranteed to be non-nil.
2688
2700
// If bounded is true then this address does not require a nil check for its operand
2689
2701
// even if that would otherwise be implied.
2690
- func (s * state ) addr (n * Node , bounded bool ) * ssa.Value {
2702
+ func (s * state ) addr (n * Node , bounded bool ) ( * ssa.Value , bool ) {
2691
2703
t := Ptrto (n .Type )
2692
2704
switch n .Op {
2693
2705
case ONAME :
@@ -2700,41 +2712,41 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
2700
2712
if n .Xoffset != 0 {
2701
2713
v = s .entryNewValue1I (ssa .OpOffPtr , v .Type , n .Xoffset , v )
2702
2714
}
2703
- return v
2715
+ return v , false
2704
2716
case PPARAM :
2705
2717
// parameter slot
2706
2718
v := s .decladdrs [n ]
2707
2719
if v != nil {
2708
- return v
2720
+ return v , false
2709
2721
}
2710
2722
if n .String () == ".fp" {
2711
2723
// Special arg that points to the frame pointer.
2712
2724
// (Used by the race detector, others?)
2713
2725
aux := s .lookupSymbol (n , & ssa.ArgSymbol {Typ : n .Type , Node : n })
2714
- return s .entryNewValue1A (ssa .OpAddr , t , aux , s .sp )
2726
+ return s .entryNewValue1A (ssa .OpAddr , t , aux , s .sp ), false
2715
2727
}
2716
2728
s .Fatalf ("addr of undeclared ONAME %v. declared: %v" , n , s .decladdrs )
2717
- return nil
2729
+ return nil , false
2718
2730
case PAUTO :
2719
2731
aux := s .lookupSymbol (n , & ssa.AutoSymbol {Typ : n .Type , Node : n })
2720
- return s .newValue1A (ssa .OpAddr , t , aux , s .sp )
2732
+ return s .newValue1A (ssa .OpAddr , t , aux , s .sp ), false
2721
2733
case PPARAMOUT : // Same as PAUTO -- cannot generate LEA early.
2722
2734
// ensure that we reuse symbols for out parameters so
2723
2735
// that cse works on their addresses
2724
2736
aux := s .lookupSymbol (n , & ssa.ArgSymbol {Typ : n .Type , Node : n })
2725
- return s .newValue1A (ssa .OpAddr , t , aux , s .sp )
2737
+ return s .newValue1A (ssa .OpAddr , t , aux , s .sp ), false
2726
2738
default :
2727
2739
s .Unimplementedf ("variable address class %v not implemented" , classnames [n .Class ])
2728
- return nil
2740
+ return nil , false
2729
2741
}
2730
2742
case OINDREG :
2731
2743
// indirect off a register
2732
2744
// used for storing/loading arguments/returns to/from callees
2733
2745
if int (n .Reg ) != Thearch .REGSP {
2734
2746
s .Unimplementedf ("OINDREG of non-SP register %s in addr: %v" , obj .Rconv (int (n .Reg )), n )
2735
- return nil
2747
+ return nil , false
2736
2748
}
2737
- return s .entryNewValue1I (ssa .OpOffPtr , t , n .Xoffset , s .sp )
2749
+ return s .entryNewValue1I (ssa .OpOffPtr , t , n .Xoffset , s .sp ), true
2738
2750
case OINDEX :
2739
2751
if n .Left .Type .IsSlice () {
2740
2752
a := s .expr (n .Left )
@@ -2745,37 +2757,37 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
2745
2757
s .boundsCheck (i , len )
2746
2758
}
2747
2759
p := s .newValue1 (ssa .OpSlicePtr , t , a )
2748
- return s .newValue2 (ssa .OpPtrIndex , t , p , i )
2760
+ return s .newValue2 (ssa .OpPtrIndex , t , p , i ), false
2749
2761
} else { // array
2750
- a := s .addr (n .Left , bounded )
2762
+ a , isVolatile := s .addr (n .Left , bounded )
2751
2763
i := s .expr (n .Right )
2752
2764
i = s .extendIndex (i )
2753
2765
len := s .constInt (Types [TINT ], n .Left .Type .NumElem ())
2754
2766
if ! n .Bounded {
2755
2767
s .boundsCheck (i , len )
2756
2768
}
2757
- return s .newValue2 (ssa .OpPtrIndex , Ptrto (n .Left .Type .Elem ()), a , i )
2769
+ return s .newValue2 (ssa .OpPtrIndex , Ptrto (n .Left .Type .Elem ()), a , i ), isVolatile
2758
2770
}
2759
2771
case OIND :
2760
- return s .exprPtr (n .Left , bounded , n .Lineno )
2772
+ return s .exprPtr (n .Left , bounded , n .Lineno ), false
2761
2773
case ODOT :
2762
- p := s .addr (n .Left , bounded )
2763
- return s .newValue1I (ssa .OpOffPtr , t , n .Xoffset , p )
2774
+ p , isVolatile := s .addr (n .Left , bounded )
2775
+ return s .newValue1I (ssa .OpOffPtr , t , n .Xoffset , p ), isVolatile
2764
2776
case ODOTPTR :
2765
2777
p := s .exprPtr (n .Left , bounded , n .Lineno )
2766
- return s .newValue1I (ssa .OpOffPtr , t , n .Xoffset , p )
2778
+ return s .newValue1I (ssa .OpOffPtr , t , n .Xoffset , p ), false
2767
2779
case OCLOSUREVAR :
2768
2780
return s .newValue1I (ssa .OpOffPtr , t , n .Xoffset ,
2769
- s .entryNewValue0 (ssa .OpGetClosurePtr , Ptrto (Types [TUINT8 ])))
2781
+ s .entryNewValue0 (ssa .OpGetClosurePtr , Ptrto (Types [TUINT8 ]))), false
2770
2782
case OCONVNOP :
2771
- addr := s .addr (n .Left , bounded )
2772
- return s .newValue1 (ssa .OpCopy , t , addr ) // ensure that addr has the right type
2783
+ addr , isVolatile := s .addr (n .Left , bounded )
2784
+ return s .newValue1 (ssa .OpCopy , t , addr ), isVolatile // ensure that addr has the right type
2773
2785
case OCALLFUNC , OCALLINTER , OCALLMETH :
2774
- return s .call (n , callNormal )
2786
+ return s .call (n , callNormal ), true
2775
2787
2776
2788
default :
2777
2789
s .Unimplementedf ("unhandled addr %v" , n .Op )
2778
- return nil
2790
+ return nil , false
2779
2791
}
2780
2792
}
2781
2793
@@ -3007,7 +3019,7 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val
3007
3019
3008
3020
// insertWBmove inserts the assignment *left = *right including a write barrier.
3009
3021
// t is the type being assigned.
3010
- func (s * state ) insertWBmove (t * Type , left , right * ssa.Value , line int32 ) {
3022
+ func (s * state ) insertWBmove (t * Type , left , right * ssa.Value , line int32 , rightIsVolatile bool ) {
3011
3023
// if writeBarrier.enabled {
3012
3024
// typedmemmove(&t, left, right)
3013
3025
// } else {
@@ -3038,8 +3050,25 @@ func (s *state) insertWBmove(t *Type, left, right *ssa.Value, line int32) {
3038
3050
b .AddEdgeTo (bElse )
3039
3051
3040
3052
s .startBlock (bThen )
3041
- taddr := s .newValue1A (ssa .OpAddr , Types [TUINTPTR ], & ssa.ExternSymbol {Typ : Types [TUINTPTR ], Sym : typenamesym (t )}, s .sb )
3042
- s .rtcall (typedmemmove , true , nil , taddr , left , right )
3053
+
3054
+ if ! rightIsVolatile {
3055
+ // Issue typedmemmove call.
3056
+ taddr := s .newValue1A (ssa .OpAddr , Types [TUINTPTR ], & ssa.ExternSymbol {Typ : Types [TUINTPTR ], Sym : typenamesym (t )}, s .sb )
3057
+ s .rtcall (typedmemmove , true , nil , taddr , left , right )
3058
+ } else {
3059
+ // Copy to temp location if the source is volatile (will be clobbered by
3060
+ // a function call). Marshaling the args to typedmemmove might clobber the
3061
+ // value we're trying to move.
3062
+ tmp := temp (t )
3063
+ s .vars [& memVar ] = s .newValue1A (ssa .OpVarDef , ssa .TypeMem , tmp , s .mem ())
3064
+ tmpaddr , _ := s .addr (tmp , true )
3065
+ s .vars [& memVar ] = s .newValue3I (ssa .OpMove , ssa .TypeMem , t .Size (), tmpaddr , right , s .mem ())
3066
+ // Issue typedmemmove call.
3067
+ taddr := s .newValue1A (ssa .OpAddr , Types [TUINTPTR ], & ssa.ExternSymbol {Typ : Types [TUINTPTR ], Sym : typenamesym (t )}, s .sb )
3068
+ s .rtcall (typedmemmove , true , nil , taddr , left , tmpaddr )
3069
+ // Mark temp as dead.
3070
+ s .vars [& memVar ] = s .newValue1A (ssa .OpVarKill , ssa .TypeMem , tmp , s .mem ())
3071
+ }
3043
3072
s .endBlock ().AddEdgeTo (bEnd )
3044
3073
3045
3074
s .startBlock (bElse )
0 commit comments