diff options
| author | David Chase <drchase@google.com> | 2025-02-19 16:47:31 -0500 |
|---|---|---|
| committer | David Chase <drchase@google.com> | 2025-02-25 08:35:38 -0800 |
| commit | c2ae5c7443fc8bda1d2b06390d4b439e81fb4b09 (patch) | |
| tree | ac228294c6dd1555af397d362b135aa6695e32f3 /src/cmd | |
| parent | 6adf08f747aff60810e754ca74e1bef381cbae86 (diff) | |
| download | go-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.go | 19 | ||||
| -rw-r--r-- | src/cmd/compile/internal/arm/ssa.go | 19 | ||||
| -rw-r--r-- | src/cmd/compile/internal/arm64/ssa.go | 19 | ||||
| -rw-r--r-- | src/cmd/compile/internal/loong64/ssa.go | 17 | ||||
| -rw-r--r-- | src/cmd/compile/internal/mips/ssa.go | 17 | ||||
| -rw-r--r-- | src/cmd/compile/internal/mips64/ssa.go | 17 | ||||
| -rw-r--r-- | src/cmd/compile/internal/ppc64/ssa.go | 21 | ||||
| -rw-r--r-- | src/cmd/compile/internal/riscv64/ssa.go | 17 | ||||
| -rw-r--r-- | src/cmd/compile/internal/s390x/ssa.go | 15 | ||||
| -rw-r--r-- | src/cmd/compile/internal/ssa/_gen/genericOps.go | 22 | ||||
| -rw-r--r-- | src/cmd/compile/internal/ssa/func.go | 12 | ||||
| -rw-r--r-- | src/cmd/compile/internal/ssagen/ssa.go | 41 | ||||
| -rw-r--r-- | src/cmd/compile/internal/wasm/ssa.go | 14 | ||||
| -rw-r--r-- | src/cmd/compile/internal/x86/ssa.go | 19 | ||||
| -rw-r--r-- | src/cmd/internal/obj/x86/obj6.go | 3 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/pcln.go | 16 |
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: |
