@@ -68,6 +68,7 @@ func initssaconfig() {
68
68
assertI2I = sysfunc ("assertI2I" )
69
69
assertI2I2 = sysfunc ("assertI2I2" )
70
70
deferproc = sysfunc ("deferproc" )
71
+ deferprocStack = sysfunc ("deferprocStack" )
71
72
Deferreturn = sysfunc ("deferreturn" )
72
73
Duffcopy = sysvar ("duffcopy" ) // asm func with special ABI
73
74
Duffzero = sysvar ("duffzero" ) // asm func with special ABI
@@ -864,7 +865,11 @@ func (s *state) stmt(n *Node) {
864
865
}
865
866
}
866
867
case ODEFER :
867
- s .call (n .Left , callDefer )
868
+ d := callDefer
869
+ if n .Esc == EscNever {
870
+ d = callDeferStack
871
+ }
872
+ s .call (n .Left , d )
868
873
case OGO :
869
874
s .call (n .Left , callGo )
870
875
@@ -2859,6 +2864,7 @@ type callKind int8
2859
2864
const (
2860
2865
callNormal callKind = iota
2861
2866
callDefer
2867
+ callDeferStack
2862
2868
callGo
2863
2869
)
2864
2870
@@ -3799,74 +3805,132 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
3799
3805
rcvr = s .newValue1 (ssa .OpIData , types .Types [TUINTPTR ], i )
3800
3806
}
3801
3807
dowidth (fn .Type )
3802
- stksize := fn .Type .ArgWidth () // includes receiver
3808
+ stksize := fn .Type .ArgWidth () // includes receiver, args, and results
3803
3809
3804
3810
// Run all assignments of temps.
3805
3811
// The temps are introduced to avoid overwriting argument
3806
3812
// slots when arguments themselves require function calls.
3807
3813
s .stmtList (n .List )
3808
3814
3809
- // Store arguments to stack, including defer/go arguments and receiver for method calls.
3810
- // These are written in SP-offset order.
3811
- argStart := Ctxt .FixedFrameSize ()
3812
- // Defer/go args.
3813
- if k != callNormal {
3814
- // Write argsize and closure (args to newproc/deferproc).
3815
- argsize := s .constInt32 (types .Types [TUINT32 ], int32 (stksize ))
3816
- addr := s .constOffPtrSP (s .f .Config .Types .UInt32Ptr , argStart )
3817
- s .store (types .Types [TUINT32 ], addr , argsize )
3818
- addr = s .constOffPtrSP (s .f .Config .Types .UintptrPtr , argStart + int64 (Widthptr ))
3819
- s .store (types .Types [TUINTPTR ], addr , closure )
3820
- stksize += 2 * int64 (Widthptr )
3821
- argStart += 2 * int64 (Widthptr )
3822
- }
3823
-
3824
- // Set receiver (for interface calls).
3825
- if rcvr != nil {
3826
- addr := s .constOffPtrSP (s .f .Config .Types .UintptrPtr , argStart )
3827
- s .store (types .Types [TUINTPTR ], addr , rcvr )
3828
- }
3829
-
3830
- // Write args.
3831
- t := n .Left .Type
3832
- args := n .Rlist .Slice ()
3833
- if n .Op == OCALLMETH {
3834
- f := t .Recv ()
3835
- s .storeArg (args [0 ], f .Type , argStart + f .Offset )
3836
- args = args [1 :]
3837
- }
3838
- for i , n := range args {
3839
- f := t .Params ().Field (i )
3840
- s .storeArg (n , f .Type , argStart + f .Offset )
3841
- }
3842
-
3843
- // call target
3844
3815
var call * ssa.Value
3845
- switch {
3846
- case k == callDefer :
3847
- call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , deferproc , s .mem ())
3848
- case k == callGo :
3849
- call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , newproc , s .mem ())
3850
- case closure != nil :
3851
- // rawLoad because loading the code pointer from a
3852
- // closure is always safe, but IsSanitizerSafeAddr
3853
- // can't always figure that out currently, and it's
3854
- // critical that we not clobber any arguments already
3855
- // stored onto the stack.
3856
- codeptr = s .rawLoad (types .Types [TUINTPTR ], closure )
3857
- call = s .newValue3 (ssa .OpClosureCall , types .TypeMem , codeptr , closure , s .mem ())
3858
- case codeptr != nil :
3859
- call = s .newValue2 (ssa .OpInterCall , types .TypeMem , codeptr , s .mem ())
3860
- case sym != nil :
3861
- call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , sym .Linksym (), s .mem ())
3862
- default :
3863
- Fatalf ("bad call type %v %v" , n .Op , n )
3816
+ if k == callDeferStack {
3817
+ // Make a defer struct d on the stack.
3818
+ t := deferstruct (stksize )
3819
+ d := tempAt (n .Pos , s .curfn , t )
3820
+
3821
+ s .vars [& memVar ] = s .newValue1A (ssa .OpVarDef , types .TypeMem , d , s .mem ())
3822
+ addr := s .addr (d , false )
3823
+
3824
+ // Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer.
3825
+ // 0: siz
3826
+ s .store (types .Types [TUINT32 ],
3827
+ s .newValue1I (ssa .OpOffPtr , types .Types [TUINT32 ].PtrTo (), t .FieldOff (0 ), addr ),
3828
+ s .constInt32 (types .Types [TUINT32 ], int32 (stksize )))
3829
+ // 1: started, set in deferprocStack
3830
+ // 2: heap, set in deferprocStack
3831
+ // 3: sp, set in deferprocStack
3832
+ // 4: pc, set in deferprocStack
3833
+ // 5: fn
3834
+ s .store (closure .Type ,
3835
+ s .newValue1I (ssa .OpOffPtr , closure .Type .PtrTo (), t .FieldOff (5 ), addr ),
3836
+ closure )
3837
+ // 6: panic, set in deferprocStack
3838
+ // 7: link, set in deferprocStack
3839
+
3840
+ // Then, store all the arguments of the defer call.
3841
+ ft := fn .Type
3842
+ off := t .FieldOff (8 )
3843
+ args := n .Rlist .Slice ()
3844
+
3845
+ // Set receiver (for interface calls). Always a pointer.
3846
+ if rcvr != nil {
3847
+ p := s .newValue1I (ssa .OpOffPtr , ft .Recv ().Type .PtrTo (), off , addr )
3848
+ s .store (types .Types [TUINTPTR ], p , rcvr )
3849
+ }
3850
+ // Set receiver (for method calls).
3851
+ if n .Op == OCALLMETH {
3852
+ f := ft .Recv ()
3853
+ s .storeArgWithBase (args [0 ], f .Type , addr , off + f .Offset )
3854
+ args = args [1 :]
3855
+ }
3856
+ // Set other args.
3857
+ for _ , f := range ft .Params ().Fields ().Slice () {
3858
+ s .storeArgWithBase (args [0 ], f .Type , addr , off + f .Offset )
3859
+ args = args [1 :]
3860
+ }
3861
+
3862
+ // Call runtime.deferprocStack with pointer to _defer record.
3863
+ arg0 := s .constOffPtrSP (types .Types [TUINTPTR ], Ctxt .FixedFrameSize ())
3864
+ s .store (types .Types [TUINTPTR ], arg0 , addr )
3865
+ call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , deferprocStack , s .mem ())
3866
+ if stksize < int64 (Widthptr ) {
3867
+ // We need room for both the call to deferprocStack and the call to
3868
+ // the deferred function.
3869
+ stksize = int64 (Widthptr )
3870
+ }
3871
+ call .AuxInt = stksize
3872
+ } else {
3873
+ // Store arguments to stack, including defer/go arguments and receiver for method calls.
3874
+ // These are written in SP-offset order.
3875
+ argStart := Ctxt .FixedFrameSize ()
3876
+ // Defer/go args.
3877
+ if k != callNormal {
3878
+ // Write argsize and closure (args to newproc/deferproc).
3879
+ argsize := s .constInt32 (types .Types [TUINT32 ], int32 (stksize ))
3880
+ addr := s .constOffPtrSP (s .f .Config .Types .UInt32Ptr , argStart )
3881
+ s .store (types .Types [TUINT32 ], addr , argsize )
3882
+ addr = s .constOffPtrSP (s .f .Config .Types .UintptrPtr , argStart + int64 (Widthptr ))
3883
+ s .store (types .Types [TUINTPTR ], addr , closure )
3884
+ stksize += 2 * int64 (Widthptr )
3885
+ argStart += 2 * int64 (Widthptr )
3886
+ }
3887
+
3888
+ // Set receiver (for interface calls).
3889
+ if rcvr != nil {
3890
+ addr := s .constOffPtrSP (s .f .Config .Types .UintptrPtr , argStart )
3891
+ s .store (types .Types [TUINTPTR ], addr , rcvr )
3892
+ }
3893
+
3894
+ // Write args.
3895
+ t := n .Left .Type
3896
+ args := n .Rlist .Slice ()
3897
+ if n .Op == OCALLMETH {
3898
+ f := t .Recv ()
3899
+ s .storeArg (args [0 ], f .Type , argStart + f .Offset )
3900
+ args = args [1 :]
3901
+ }
3902
+ for i , n := range args {
3903
+ f := t .Params ().Field (i )
3904
+ s .storeArg (n , f .Type , argStart + f .Offset )
3905
+ }
3906
+
3907
+ // call target
3908
+ switch {
3909
+ case k == callDefer :
3910
+ call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , deferproc , s .mem ())
3911
+ case k == callGo :
3912
+ call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , newproc , s .mem ())
3913
+ case closure != nil :
3914
+ // rawLoad because loading the code pointer from a
3915
+ // closure is always safe, but IsSanitizerSafeAddr
3916
+ // can't always figure that out currently, and it's
3917
+ // critical that we not clobber any arguments already
3918
+ // stored onto the stack.
3919
+ codeptr = s .rawLoad (types .Types [TUINTPTR ], closure )
3920
+ call = s .newValue3 (ssa .OpClosureCall , types .TypeMem , codeptr , closure , s .mem ())
3921
+ case codeptr != nil :
3922
+ call = s .newValue2 (ssa .OpInterCall , types .TypeMem , codeptr , s .mem ())
3923
+ case sym != nil :
3924
+ call = s .newValue1A (ssa .OpStaticCall , types .TypeMem , sym .Linksym (), s .mem ())
3925
+ default :
3926
+ Fatalf ("bad call type %v %v" , n .Op , n )
3927
+ }
3928
+ call .AuxInt = stksize // Call operations carry the argsize of the callee along with them
3864
3929
}
3865
- call .AuxInt = stksize // Call operations carry the argsize of the callee along with them
3866
3930
s .vars [& memVar ] = call
3867
3931
3868
3932
// Finish block for defers
3869
- if k == callDefer {
3933
+ if k == callDefer || k == callDeferStack {
3870
3934
b := s .endBlock ()
3871
3935
b .Kind = ssa .BlockDefer
3872
3936
b .SetControl (call )
@@ -4361,17 +4425,27 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
4361
4425
}
4362
4426
4363
4427
func (s * state ) storeArg (n * Node , t * types.Type , off int64 ) {
4428
+ s .storeArgWithBase (n , t , s .sp , off )
4429
+ }
4430
+
4431
+ func (s * state ) storeArgWithBase (n * Node , t * types.Type , base * ssa.Value , off int64 ) {
4364
4432
pt := types .NewPtr (t )
4365
- sp := s .constOffPtrSP (pt , off )
4433
+ var addr * ssa.Value
4434
+ if base == s .sp {
4435
+ // Use special routine that avoids allocation on duplicate offsets.
4436
+ addr = s .constOffPtrSP (pt , off )
4437
+ } else {
4438
+ addr = s .newValue1I (ssa .OpOffPtr , pt , off , base )
4439
+ }
4366
4440
4367
4441
if ! canSSAType (t ) {
4368
4442
a := s .addr (n , false )
4369
- s .move (t , sp , a )
4443
+ s .move (t , addr , a )
4370
4444
return
4371
4445
}
4372
4446
4373
4447
a := s .expr (n )
4374
- s .storeType (t , sp , a , 0 , false )
4448
+ s .storeType (t , addr , a , 0 , false )
4375
4449
}
4376
4450
4377
4451
// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
0 commit comments