@@ -68,6 +68,7 @@ func initssaconfig() {
6868 assertI2I = sysfunc ("assertI2I" )
6969 assertI2I2 = sysfunc ("assertI2I2" )
7070 deferproc = sysfunc ("deferproc" )
71+ deferprocStack = sysfunc ("deferprocStack" )
7172 Deferreturn = sysfunc ("deferreturn" )
7273 Duffcopy = sysvar ("duffcopy" ) // asm func with special ABI
7374 Duffzero = sysvar ("duffzero" ) // asm func with special ABI
@@ -864,7 +865,11 @@ func (s *state) stmt(n *Node) {
864865 }
865866 }
866867 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 )
868873 case OGO :
869874 s .call (n .Left , callGo )
870875
@@ -2859,6 +2864,7 @@ type callKind int8
28592864const (
28602865 callNormal callKind = iota
28612866 callDefer
2867+ callDeferStack
28622868 callGo
28632869)
28642870
@@ -3799,74 +3805,132 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
37993805 rcvr = s .newValue1 (ssa .OpIData , types .Types [TUINTPTR ], i )
38003806 }
38013807 dowidth (fn .Type )
3802- stksize := fn .Type .ArgWidth () // includes receiver
3808+ stksize := fn .Type .ArgWidth () // includes receiver, args, and results
38033809
38043810 // Run all assignments of temps.
38053811 // The temps are introduced to avoid overwriting argument
38063812 // slots when arguments themselves require function calls.
38073813 s .stmtList (n .List )
38083814
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
38443815 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
38643929 }
3865- call .AuxInt = stksize // Call operations carry the argsize of the callee along with them
38663930 s .vars [& memVar ] = call
38673931
38683932 // Finish block for defers
3869- if k == callDefer {
3933+ if k == callDefer || k == callDeferStack {
38703934 b := s .endBlock ()
38713935 b .Kind = ssa .BlockDefer
38723936 b .SetControl (call )
@@ -4361,17 +4425,27 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
43614425}
43624426
43634427func (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 ) {
43644432 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+ }
43664440
43674441 if ! canSSAType (t ) {
43684442 a := s .addr (n , false )
4369- s .move (t , sp , a )
4443+ s .move (t , addr , a )
43704444 return
43714445 }
43724446
43734447 a := s .expr (n )
4374- s .storeType (t , sp , a , 0 , false )
4448+ s .storeType (t , addr , a , 0 , false )
43754449}
43764450
43774451// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
0 commit comments