@@ -410,6 +410,8 @@ func buildssa(fn *ir.Func, worker int, isPgoHot bool) *ssa.Func {
410
410
// Don't support open-coded defers for 386 ONLY when using shared
411
411
// libraries, because there is extra code (added by rewriteToUseGot())
412
412
// preceding the deferreturn/ret code that we don't track correctly.
413
+ //
414
+ // TODO this restriction can be removed given adjusted offset in computeDeferReturn in cmd/link/internal/ld/pcln.go
413
415
s .hasOpenDefers = false
414
416
}
415
417
if s .hasOpenDefers && s .instrumentEnterExit {
@@ -2166,7 +2168,17 @@ func (s *state) exit() *ssa.Block {
2166
2168
}
2167
2169
s .openDeferExit ()
2168
2170
} else {
2171
+ // Shared deferreturn is assigned the "last" position in the function.
2172
+ // The linker picks the first deferreturn call it sees, so this is
2173
+ // the only sensible "shared" place.
2174
+ // To not-share deferreturn, the protocol would need to be changed
2175
+ // so that the call to deferproc-etc would receive the PC offset from
2176
+ // the return PC, and the runtime would need to use that instead of
2177
+ // the deferreturn retrieved from the pcln information.
2178
+ // opendefers would remain a problem, however.
2179
+ s .pushLine (s .curfn .Endlineno )
2169
2180
s .rtcall (ir .Syms .Deferreturn , true , nil )
2181
+ s .popLine ()
2170
2182
}
2171
2183
}
2172
2184
@@ -4411,6 +4423,8 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExt
4411
4423
s .Fatalf ("go/defer call with arguments: %v" , n )
4412
4424
}
4413
4425
4426
+ isCallDeferRangeFunc := false
4427
+
4414
4428
switch n .Op () {
4415
4429
case ir .OCALLFUNC :
4416
4430
if (k == callNormal || k == callTail ) && fn .Op () == ir .ONAME && fn .(* ir.Name ).Class == ir .PFUNC {
@@ -4434,7 +4448,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExt
4434
4448
}
4435
4449
}
4436
4450
if fn := n .Fun .Sym ().Name ; n .Fun .Sym ().Pkg == ir .Pkgs .Runtime && fn == "deferrangefunc" {
4437
- s . f . HasDeferRangeFunc = true
4451
+ isCallDeferRangeFunc = true
4438
4452
}
4439
4453
break
4440
4454
}
@@ -4596,17 +4610,20 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExt
4596
4610
}
4597
4611
4598
4612
// Finish block for defers
4599
- if k == callDefer || k == callDeferStack {
4613
+ if k == callDefer || k == callDeferStack || isCallDeferRangeFunc {
4600
4614
b := s .endBlock ()
4601
4615
b .Kind = ssa .BlockDefer
4602
4616
b .SetControl (call )
4603
4617
bNext := s .f .NewBlock (ssa .BlockPlain )
4604
4618
b .AddEdgeTo (bNext )
4605
- // Add recover edge to exit code.
4606
- r := s .f .NewBlock (ssa .BlockPlain )
4607
- s .startBlock (r )
4608
- s .exit ()
4609
- b .AddEdgeTo (r )
4619
+ r := s .f .DeferReturn // Share a single deferreturn among all defers
4620
+ if r == nil {
4621
+ r = s .f .NewBlock (ssa .BlockPlain )
4622
+ s .startBlock (r )
4623
+ s .exit ()
4624
+ s .f .DeferReturn = r
4625
+ }
4626
+ b .AddEdgeTo (r ) // Add recover edge to exit code. This is a fake edge to keep the block live.
4610
4627
b .Likely = ssa .BranchLikely
4611
4628
s .startBlock (bNext )
4612
4629
}
@@ -6571,13 +6588,15 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
6571
6588
// nop (which will never execute) after the call.
6572
6589
Arch .Ginsnop (s .pp )
6573
6590
}
6574
- if openDeferInfo != nil || f . HasDeferRangeFunc {
6591
+ if openDeferInfo != nil {
6575
6592
// When doing open-coded defers, generate a disconnected call to
6576
6593
// deferreturn and a return. This will be used to during panic
6577
6594
// recovery to unwind the stack and return back to the runtime.
6578
- //
6579
- // deferrangefunc needs to be sure that at least one of these exists;
6580
- // if all returns are dead-code eliminated, there might not be.
6595
+
6596
+ // Note that this exit code doesn't work if a return parameter
6597
+ // is heap-allocated, but open defers aren't enabled in that case.
6598
+
6599
+ // TODO either make this handle heap-allocated return parameters or reuse the other-defers general-purpose code path.
6581
6600
s .pp .NextLive = s .livenessMap .DeferReturn
6582
6601
p := s .pp .Prog (obj .ACALL )
6583
6602
p .To .Type = obj .TYPE_MEM
0 commit comments