Skip to content

Commit c10b980

Browse files
committed
cmd/compile: restore tail call for method wrappers
For certain type of method wrappers we used to generate a tail call. That was disabled in CL 307234 when register ABI is used, because with the current IR it was difficult to generate a tail call with the arguments in the right places. The problem was that the IR does not contain a CALL-like node with arguments; instead, it contains an OAS node that adjusts the receiver, than an OTAILCALL node that just contains the target, but no argument (with the assumption that the OAS node will put the adjusted receiver in the right place). With register ABI, putting arguments in registers are done in SSA. The assignment (OAS) doesn't put the receiver in register. This CL changes the IR of a tail call to take an actual OCALL node. Specifically, a tail call is represented as OTAILCALL (OCALL target args...) This way, the call target and args are connected through the OCALL node. So the call can be analyzed in SSA and the args can be passed in the right places. (Alternatively, we could have OTAILCALL node directly take the target and the args, without the OCALL node. Using an OCALL node is convenient as there are existing code that processes OCALL nodes which do not need to be changed. Also, a tail call is similar to ORETURN (OCALL target args...), except it doesn't preserve the frame. I did the former but I'm open to change.) The SSA representation is similar. Previously, the IR lowers to a Store the receiver then a BlockRetJmp which jumps to the target (without putting the arg in register). Now we use a TailCall op, which takes the target and the args. The call expansion pass and the register allocator handles TailCall pretty much like a StaticCall, and it will do the right ABI analysis and put the args in the right places. (Args other than the receiver are already in the right places. For register args it generates no code for them. For stack args currently it generates a self copy. I'll work on optimize that out.) BlockRetJmp is still used, signaling it is a tail call. The actual call is made in the TailCall op so BlockRetJmp generates no code (we could use BlockExit if we like). This slightly reduces binary size: old new cmd/go 14003088 13953936 cmd/link 6275552 6271456 Change-Id: I2d16d8d419fe1f17554916d317427383e17e27f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/350145 Trust: Cherry Mui <[email protected]> Run-TryBot: Cherry Mui <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 50e4508 commit c10b980

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+319
-121
lines changed

src/cmd/compile/internal/amd64/ssa.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
10081008
}
10091009
r := v.Reg()
10101010
getgFromTLS(s, r)
1011-
case ssa.OpAMD64CALLstatic:
1011+
case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail:
10121012
if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
10131013
// zeroing X15 when entering ABIInternal from ABI0
10141014
if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
@@ -1017,6 +1017,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
10171017
// set G register from TLS
10181018
getgFromTLS(s, x86.REG_R14)
10191019
}
1020+
if v.Op == ssa.OpAMD64CALLtail {
1021+
s.TailCall(v)
1022+
break
1023+
}
10201024
s.Call(v)
10211025
if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
10221026
// zeroing X15 when entering ABIInternal from ABI0
@@ -1314,22 +1318,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
13141318
p.To.Type = obj.TYPE_BRANCH
13151319
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
13161320
}
1317-
case ssa.BlockExit:
1321+
case ssa.BlockExit, ssa.BlockRetJmp:
13181322
case ssa.BlockRet:
13191323
s.Prog(obj.ARET)
1320-
case ssa.BlockRetJmp:
1321-
if s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
1322-
// zeroing X15 when entering ABIInternal from ABI0
1323-
if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
1324-
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
1325-
}
1326-
// set G register from TLS
1327-
getgFromTLS(s, x86.REG_R14)
1328-
}
1329-
p := s.Prog(obj.ARET)
1330-
p.To.Type = obj.TYPE_MEM
1331-
p.To.Name = obj.NAME_EXTERN
1332-
p.To.Sym = b.Aux.(*obj.LSym)
13331324

13341325
case ssa.BlockAMD64EQF:
13351326
s.CombJump(b, next, &eqfJumps)

src/cmd/compile/internal/arm/ssa.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
696696
p.To.Reg = v.Reg()
697697
case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter:
698698
s.Call(v)
699+
case ssa.OpARMCALLtail:
700+
s.TailCall(v)
699701
case ssa.OpARMCALLudiv:
700702
p := s.Prog(obj.ACALL)
701703
p.To.Type = obj.TYPE_MEM
@@ -936,17 +938,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
936938
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
937939
}
938940

939-
case ssa.BlockExit:
941+
case ssa.BlockExit, ssa.BlockRetJmp:
940942

941943
case ssa.BlockRet:
942944
s.Prog(obj.ARET)
943945

944-
case ssa.BlockRetJmp:
945-
p := s.Prog(obj.ARET)
946-
p.To.Type = obj.TYPE_MEM
947-
p.To.Name = obj.NAME_EXTERN
948-
p.To.Sym = b.Aux.(*obj.LSym)
949-
950946
case ssa.BlockARMEQ, ssa.BlockARMNE,
951947
ssa.BlockARMLT, ssa.BlockARMGE,
952948
ssa.BlockARMLE, ssa.BlockARMGT,

src/cmd/compile/internal/arm64/ssa.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
10461046
p4.To.SetTarget(p)
10471047
case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
10481048
s.Call(v)
1049+
case ssa.OpARM64CALLtail:
1050+
s.TailCall(v)
10491051
case ssa.OpARM64LoweredWB:
10501052
p := s.Prog(obj.ACALL)
10511053
p.To.Type = obj.TYPE_MEM
@@ -1241,17 +1243,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
12411243
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
12421244
}
12431245

1244-
case ssa.BlockExit:
1246+
case ssa.BlockExit, ssa.BlockRetJmp:
12451247

12461248
case ssa.BlockRet:
12471249
s.Prog(obj.ARET)
12481250

1249-
case ssa.BlockRetJmp:
1250-
p := s.Prog(obj.ARET)
1251-
p.To.Type = obj.TYPE_MEM
1252-
p.To.Name = obj.NAME_EXTERN
1253-
p.To.Sym = b.Aux.(*obj.LSym)
1254-
12551251
case ssa.BlockARM64EQ, ssa.BlockARM64NE,
12561252
ssa.BlockARM64LT, ssa.BlockARM64GE,
12571253
ssa.BlockARM64LE, ssa.BlockARM64GT,

src/cmd/compile/internal/escape/stmt.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ func (e *escape) stmt(n ir.Node) {
180180
e.goDeferStmt(n)
181181

182182
case ir.OTAILCALL:
183-
// TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it.
183+
n := n.(*ir.TailCallStmt)
184+
e.call(nil, n.Call)
184185
}
185186
}
186187

src/cmd/compile/internal/inline/inl.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
544544
call := call.(*ir.CallExpr)
545545
call.NoInline = true
546546
}
547+
case ir.OTAILCALL:
548+
n := n.(*ir.TailCallStmt)
549+
n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)?
547550

548551
// TODO do them here (or earlier),
549552
// so escape analysis can avoid more heapmoves.

src/cmd/compile/internal/ir/fmt.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ func stmtFmt(n Node, s fmt.State) {
386386

387387
case OTAILCALL:
388388
n := n.(*TailCallStmt)
389-
fmt.Fprintf(s, "tailcall %v", n.Target)
389+
fmt.Fprintf(s, "tailcall %v", n.Call)
390390

391391
case OINLMARK:
392392
n := n.(*InlineMarkStmt)

src/cmd/compile/internal/ir/node_gen.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/ir/stmt.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,14 +385,11 @@ func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
385385
// code generation to jump directly to another function entirely.
386386
type TailCallStmt struct {
387387
miniStmt
388-
Target *Name
388+
Call *CallExpr // the underlying call
389389
}
390390

391-
func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt {
392-
if target.Op() != ONAME || target.Class != PFUNC {
393-
base.FatalfAt(pos, "tail call to non-func %v", target)
394-
}
395-
n := &TailCallStmt{Target: target}
391+
func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt {
392+
n := &TailCallStmt{Call: call}
396393
n.pos = pos
397394
n.op = OTAILCALL
398395
return n

src/cmd/compile/internal/mips/ssa.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
475475
p6.To.SetTarget(p2)
476476
case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter:
477477
s.Call(v)
478+
case ssa.OpMIPSCALLtail:
479+
s.TailCall(v)
478480
case ssa.OpMIPSLoweredWB:
479481
p := s.Prog(obj.ACALL)
480482
p.To.Type = obj.TYPE_MEM
@@ -841,14 +843,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
841843
p.To.Type = obj.TYPE_BRANCH
842844
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
843845
}
844-
case ssa.BlockExit:
846+
case ssa.BlockExit, ssa.BlockRetJmp:
845847
case ssa.BlockRet:
846848
s.Prog(obj.ARET)
847-
case ssa.BlockRetJmp:
848-
p := s.Prog(obj.ARET)
849-
p.To.Type = obj.TYPE_MEM
850-
p.To.Name = obj.NAME_EXTERN
851-
p.To.Sym = b.Aux.(*obj.LSym)
852849
case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
853850
ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
854851
ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,

src/cmd/compile/internal/mips64/ssa.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
491491
p6.To.SetTarget(p2)
492492
case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter:
493493
s.Call(v)
494+
case ssa.OpMIPS64CALLtail:
495+
s.TailCall(v)
494496
case ssa.OpMIPS64LoweredWB:
495497
p := s.Prog(obj.ACALL)
496498
p.To.Type = obj.TYPE_MEM
@@ -808,14 +810,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
808810
p.To.Type = obj.TYPE_BRANCH
809811
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
810812
}
811-
case ssa.BlockExit:
813+
case ssa.BlockExit, ssa.BlockRetJmp:
812814
case ssa.BlockRet:
813815
s.Prog(obj.ARET)
814-
case ssa.BlockRetJmp:
815-
p := s.Prog(obj.ARET)
816-
p.To.Type = obj.TYPE_MEM
817-
p.To.Name = obj.NAME_EXTERN
818-
p.To.Sym = b.Aux.(*obj.LSym)
819816
case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE,
820817
ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ,
821818
ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ,

0 commit comments

Comments
 (0)