aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorDavid Chase <drchase@google.com>2025-02-19 16:47:31 -0500
committerDavid Chase <drchase@google.com>2025-02-25 08:35:38 -0800
commitc2ae5c7443fc8bda1d2b06390d4b439e81fb4b09 (patch)
treeac228294c6dd1555af397d362b135aa6695e32f3 /src/cmd
parent6adf08f747aff60810e754ca74e1bef381cbae86 (diff)
downloadgo-c2ae5c7443fc8bda1d2b06390d4b439e81fb4b09.tar.xz
cmd/compile, runtime: use PC of deferreturn for panic transfer
this removes the old conditional-on-register-value handshake from the deferproc/deferprocstack logic. The "line" for the recovery-exit frame itself (not the defers that it runs) is the closing brace of the function. Reduces code size slightly (e.g. go command is 0.2% smaller) Sample output showing effect of this change, also what sort of code it requires to observe the effect: ``` package main import "os" func main() { g(len(os.Args) - 1) // stack[0] } var gi int var pi *int = &gi //go:noinline func g(i int) { switch i { case 0: defer func() { println("g0", i) q() // stack[2] if i == 0 }() for j := *pi; j < 1; j++ { defer func() { println("recover0", recover().(string)) }() } default: for j := *pi; j < 1; j++ { defer func() { println("g1", i) q() // stack[2] if i == 1 }() } defer func() { println("recover1", recover().(string)) }() } p() } // stack[1] (deferreturn) //go:noinline func p() { panic("p()") } //go:noinline func q() { panic("q()") // stack[3] } /* Sample output for "./foo foo": recover1 p() g1 1 panic: q() goroutine 1 [running]: main.q() .../main.go:46 +0x2c main.g.func3() .../main.go:29 +0x48 main.g(0x1?) .../main.go:37 +0x68 main.main() .../main.go:6 +0x28 */ ``` Change-Id: Ie39ea62ecc244213500380ea06d44024cadc2317 Reviewed-on: https://go-review.googlesource.com/c/go/+/650795 Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/compile/internal/amd64/ssa.go19
-rw-r--r--src/cmd/compile/internal/arm/ssa.go19
-rw-r--r--src/cmd/compile/internal/arm64/ssa.go19
-rw-r--r--src/cmd/compile/internal/loong64/ssa.go17
-rw-r--r--src/cmd/compile/internal/mips/ssa.go17
-rw-r--r--src/cmd/compile/internal/mips64/ssa.go17
-rw-r--r--src/cmd/compile/internal/ppc64/ssa.go21
-rw-r--r--src/cmd/compile/internal/riscv64/ssa.go17
-rw-r--r--src/cmd/compile/internal/s390x/ssa.go15
-rw-r--r--src/cmd/compile/internal/ssa/_gen/genericOps.go22
-rw-r--r--src/cmd/compile/internal/ssa/func.go12
-rw-r--r--src/cmd/compile/internal/ssagen/ssa.go41
-rw-r--r--src/cmd/compile/internal/wasm/ssa.go14
-rw-r--r--src/cmd/compile/internal/x86/ssa.go19
-rw-r--r--src/cmd/internal/obj/x86/obj6.go3
-rw-r--r--src/cmd/link/internal/ld/pcln.go16
16 files changed, 76 insertions, 212 deletions
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 9eef71f760..332c49af00 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -1441,24 +1441,7 @@ var nefJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in rax:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(x86.ATESTL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_AX
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
- p = s.Prog(x86.AJNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 638ed3ed4e..f129ab493d 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -918,24 +918,7 @@ var gtJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
-
- case ssa.BlockDefer:
- // defer returns in R0:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(arm.ACMP)
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = 0
- p.Reg = arm.REG_R0
- p = s.Prog(arm.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
index 0f5c5a17bd..957e943e44 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -1327,24 +1327,7 @@ var gtJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
-
- case ssa.BlockDefer:
- // defer returns in R0:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(arm64.ACMP)
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = 0
- p.Reg = arm64.REG_R0
- p = s.Prog(arm64.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/loong64/ssa.go b/src/cmd/compile/internal/loong64/ssa.go
index 0ba9efa1d3..e8b8b27f87 100644
--- a/src/cmd/compile/internal/loong64/ssa.go
+++ b/src/cmd/compile/internal/loong64/ssa.go
@@ -970,22 +970,7 @@ var blockJump = map[ssa.BlockKind]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in R19:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(loong64.ABNE)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = loong64.REGZERO
- p.Reg = loong64.REG_R19
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
index bfccafd8e5..4c7c8eafcd 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -826,22 +826,7 @@ var blockJump = map[ssa.BlockKind]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in R1:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(mips.ABNE)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = mips.REGZERO
- p.Reg = mips.REG_R1
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
index 0c0dc6e495..5b5edf622a 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -835,22 +835,7 @@ var blockJump = map[ssa.BlockKind]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in R1:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(mips.ABNE)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = mips.REGZERO
- p.Reg = mips.REG_R1
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index 53ec4289c7..c1f2484bf4 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -2003,26 +2003,7 @@ var blockJump = [...]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockDefer:
- // defer returns in R3:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(ppc64.ACMP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = ppc64.REG_R3
- p.To.Type = obj.TYPE_CONST
- p.To.Offset = 0
-
- p = s.Prog(ppc64.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
-
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go
index 759d8d7cf4..636ef44d68 100644
--- a/src/cmd/compile/internal/riscv64/ssa.go
+++ b/src/cmd/compile/internal/riscv64/ssa.go
@@ -802,22 +802,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.SetPos(b.Pos)
switch b.Kind {
- case ssa.BlockDefer:
- // defer returns in A0:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(riscv.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- p.From.Type = obj.TYPE_REG
- p.From.Reg = riscv.REG_ZERO
- p.Reg = riscv.REG_A0
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index a97c1569c1..4d24881dba 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -887,26 +887,13 @@ func blockAsm(b *ssa.Block) obj.As {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
// Handle generic blocks first.
switch b.Kind {
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(s390x.ABR)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
return
- case ssa.BlockDefer:
- // defer returns in R3:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Br(s390x.ACIJ, b.Succs[1].Block())
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = int64(s390x.NotEqual & s390x.NotUnordered) // unordered is not possible
- p.Reg = s390x.REG_R3
- p.AddRestSourceConst(0)
- if b.Succs[0].Block() != next {
- s.Br(s390x.ABR, b.Succs[0].Block())
- }
- return
case ssa.BlockExit, ssa.BlockRetJmp:
return
case ssa.BlockRet:
diff --git a/src/cmd/compile/internal/ssa/_gen/genericOps.go b/src/cmd/compile/internal/ssa/_gen/genericOps.go
index 4dde6d51c5..37de6e9919 100644
--- a/src/cmd/compile/internal/ssa/_gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/_gen/genericOps.go
@@ -663,21 +663,21 @@ var genericOps = []opData{
{name: "PrefetchCacheStreamed", argLength: 2, hasSideEffects: true}, // Do non-temporal or streamed prefetch arg0 to cache. arg0=addr, arg1=memory.
}
-// kind controls successors implicit exit
-// ----------------------------------------------------------
-// Exit [return mem] [] yes
-// Ret [return mem] [] yes
-// RetJmp [return mem] [] yes
-// Plain [] [next]
-// If [boolean Value] [then, else]
-// First [] [always, never]
-// Defer [mem] [nopanic, panic] (control opcode should be OpStaticCall to runtime.deferproc)
-// JumpTable [integer Value] [succ1,succ2,..]
+// kind controls successors implicit exit
+// ------------------------------------------------------------
+// Exit [return mem] [] yes
+// Ret [return mem] [] yes
+// RetJmp [return mem] [] yes
+// Plain [] [next]
+// If [boolean Value] [then, else]
+// First [] [always, never]
+// Defer [mem] [nopanic, recovery] (control opcode should be OpStaticCall to runtime.defer*)
+// JumpTable [integer Value] [succ1,succ2,..]
var genericBlocks = []blockData{
{name: "Plain"}, // a single successor
{name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1]
- {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type)
+ {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovery branch (jmp performed by runtime). Controls[0] is call op (of memory type).
{name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result
{name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call
{name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 998cc804aa..12e4c268f0 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -41,12 +41,12 @@ type Func struct {
ABISelf *abi.ABIConfig // ABI for function being compiled
ABIDefault *abi.ABIConfig // ABI for rtcall and other no-parsed-signature/pragma functions.
- scheduled bool // Values in Blocks are in final order
- laidout bool // Blocks are ordered
- NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
- dumpFileSeq uint8 // the sequence numbers of dump file. (%s_%02d__%s.dump", funcname, dumpFileSeq, phaseName)
- IsPgoHot bool
- HasDeferRangeFunc bool // if true, needs a deferreturn so deferrangefunc can use it for recover() return PC
+ scheduled bool // Values in Blocks are in final order
+ laidout bool // Blocks are ordered
+ NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
+ dumpFileSeq uint8 // the sequence numbers of dump file. (%s_%02d__%s.dump", funcname, dumpFileSeq, phaseName)
+ IsPgoHot bool
+ DeferReturn *Block // avoid creating more than one deferreturn if there's multiple calls to deferproc-etc.
// when register allocation is done, maps value ids to locations
RegAlloc []Location
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 306244424c..07269e65f2 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -410,6 +410,8 @@ func buildssa(fn *ir.Func, worker int, isPgoHot bool) *ssa.Func {
// Don't support open-coded defers for 386 ONLY when using shared
// libraries, because there is extra code (added by rewriteToUseGot())
// preceding the deferreturn/ret code that we don't track correctly.
+ //
+ // TODO this restriction can be removed given adjusted offset in computeDeferReturn in cmd/link/internal/ld/pcln.go
s.hasOpenDefers = false
}
if s.hasOpenDefers && s.instrumentEnterExit {
@@ -2166,7 +2168,17 @@ func (s *state) exit() *ssa.Block {
}
s.openDeferExit()
} else {
+ // Shared deferreturn is assigned the "last" position in the function.
+ // The linker picks the first deferreturn call it sees, so this is
+ // the only sensible "shared" place.
+ // To not-share deferreturn, the protocol would need to be changed
+ // so that the call to deferproc-etc would receive the PC offset from
+ // the return PC, and the runtime would need to use that instead of
+ // the deferreturn retrieved from the pcln information.
+ // opendefers would remain a problem, however.
+ s.pushLine(s.curfn.Endlineno)
s.rtcall(ir.Syms.Deferreturn, true, nil)
+ s.popLine()
}
}
@@ -4411,6 +4423,8 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExt
s.Fatalf("go/defer call with arguments: %v", n)
}
+ isCallDeferRangeFunc := false
+
switch n.Op() {
case ir.OCALLFUNC:
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
}
}
if fn := n.Fun.Sym().Name; n.Fun.Sym().Pkg == ir.Pkgs.Runtime && fn == "deferrangefunc" {
- s.f.HasDeferRangeFunc = true
+ isCallDeferRangeFunc = true
}
break
}
@@ -4596,17 +4610,20 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExt
}
// Finish block for defers
- if k == callDefer || k == callDeferStack {
+ if k == callDefer || k == callDeferStack || isCallDeferRangeFunc {
b := s.endBlock()
b.Kind = ssa.BlockDefer
b.SetControl(call)
bNext := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(bNext)
- // Add recover edge to exit code.
- r := s.f.NewBlock(ssa.BlockPlain)
- s.startBlock(r)
- s.exit()
- b.AddEdgeTo(r)
+ r := s.f.DeferReturn // Share a single deferreturn among all defers
+ if r == nil {
+ r = s.f.NewBlock(ssa.BlockPlain)
+ s.startBlock(r)
+ s.exit()
+ s.f.DeferReturn = r
+ }
+ b.AddEdgeTo(r) // Add recover edge to exit code. This is a fake edge to keep the block live.
b.Likely = ssa.BranchLikely
s.startBlock(bNext)
}
@@ -6571,13 +6588,15 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
// nop (which will never execute) after the call.
Arch.Ginsnop(s.pp)
}
- if openDeferInfo != nil || f.HasDeferRangeFunc {
+ if openDeferInfo != nil {
// When doing open-coded defers, generate a disconnected call to
// deferreturn and a return. This will be used to during panic
// recovery to unwind the stack and return back to the runtime.
- //
- // deferrangefunc needs to be sure that at least one of these exists;
- // if all returns are dead-code eliminated, there might not be.
+
+ // Note that this exit code doesn't work if a return parameter
+ // is heap-allocated, but open defers aren't enabled in that case.
+
+ // TODO either make this handle heap-allocated return parameters or reuse the other-defers general-purpose code path.
s.pp.NextLive = s.livenessMap.DeferReturn
p := s.pp.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go
index 85f34a7707..daee82f1fd 100644
--- a/src/cmd/compile/internal/wasm/ssa.go
+++ b/src/cmd/compile/internal/wasm/ssa.go
@@ -169,7 +169,7 @@ func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if next != b.Succs[0].Block() {
s.Br(obj.AJMP, b.Succs[0].Block())
}
@@ -203,18 +203,6 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
case ssa.BlockExit, ssa.BlockRetJmp:
- case ssa.BlockDefer:
- p := s.Prog(wasm.AGet)
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: wasm.REG_RET0}
- s.Prog(wasm.AI64Eqz)
- s.Prog(wasm.AI32Eqz)
- s.Prog(wasm.AIf)
- s.Br(obj.AJMP, b.Succs[1].Block())
- s.Prog(wasm.AEnd)
- if next != b.Succs[0].Block() {
- s.Br(obj.AJMP, b.Succs[0].Block())
- }
-
default:
panic("unexpected block")
}
diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go
index 35ad2d90e6..347c5cb560 100644
--- a/src/cmd/compile/internal/x86/ssa.go
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -946,24 +946,7 @@ var nefJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in rax:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(x86.ATESTL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_AX
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
- p = s.Prog(x86.AJNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index e6ea8985e4..53c0918254 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -448,6 +448,9 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
//
// We disable open-coded defers in buildssa() on 386 ONLY with shared
// libraries because of this extra code added before deferreturn calls.
+ //
+ // computeDeferReturn in cmd/link/internal/ld/pcln.go depends
+ // on the size of these instructions.
if ctxt.Arch.Family == sys.AMD64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 {
return
}
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index ea08fd3d31..a09d3acd5e 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -143,8 +143,22 @@ func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
// instruction).
deferreturn = uint32(r.Off())
switch target.Arch.Family {
- case sys.AMD64, sys.I386:
+ case sys.I386:
deferreturn--
+ if ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin {
+ // In this mode, we need to get the address from GOT,
+ // with two additional instructions like
+ //
+ // CALL __x86.get_pc_thunk.bx(SB) // 5 bytes
+ // LEAL _GLOBAL_OFFSET_TABLE_<>(BX), BX // 6 bytes
+ //
+ // We need to back off to the get_pc_thunk call.
+ // (See progedit in cmd/internal/obj/x86/obj6.go)
+ deferreturn -= 11
+ }
+ case sys.AMD64:
+ deferreturn--
+
case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64:
// no change
case sys.S390X: