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,

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
18291829
case ssa.OpPPC64CALLstatic:
18301830
s.Call(v)
18311831

1832+
case ssa.OpPPC64CALLtail:
1833+
s.TailCall(v)
1834+
18321835
case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter:
18331836
p := s.Prog(ppc64.AMOVD)
18341837
p.From.Type = obj.TYPE_REG
@@ -1980,14 +1983,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
19801983
p.To.Type = obj.TYPE_BRANCH
19811984
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
19821985
}
1983-
case ssa.BlockExit:
1986+
case ssa.BlockExit, ssa.BlockRetJmp:
19841987
case ssa.BlockRet:
19851988
s.Prog(obj.ARET)
1986-
case ssa.BlockRetJmp:
1987-
p := s.Prog(obj.AJMP)
1988-
p.To.Type = obj.TYPE_MEM
1989-
p.To.Name = obj.NAME_EXTERN
1990-
p.To.Sym = b.Aux.(*obj.LSym)
19911989

19921990
case ssa.BlockPPC64EQ, ssa.BlockPPC64NE,
19931991
ssa.BlockPPC64LT, ssa.BlockPPC64GE,

src/cmd/compile/internal/reflectdata/reflect.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package reflectdata
77
import (
88
"encoding/binary"
99
"fmt"
10-
"internal/buildcfg"
1110
"os"
1211
"sort"
1312
"strings"
@@ -1869,15 +1868,11 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
18691868
// Disable tailcall for RegabiArgs for now. The IR does not connect the
18701869
// arguments with the OTAILCALL node, and the arguments are not marshaled
18711870
// correctly.
1872-
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs && !generic {
1873-
// generate tail call: adjust pointer receiver and jump to embedded method.
1874-
left := dot.X // skip final .M
1875-
if !left.Type().IsPtr() {
1876-
left = typecheck.NodAddr(left)
1877-
}
1878-
as := ir.NewAssignStmt(base.Pos, nthis, typecheck.ConvNop(left, rcvr))
1879-
fn.Body.Append(as)
1880-
fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name)))
1871+
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
1872+
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
1873+
call.Args = ir.ParamNames(tfn.Type())
1874+
call.IsDDD = tfn.Type().IsVariadic()
1875+
fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
18811876
} else {
18821877
fn.SetWrapper(true) // ignore frame for panic+recover matching
18831878
var call *ir.CallExpr

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
413413
p.To.Reg = v.Reg()
414414
case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter:
415415
s.Call(v)
416+
case ssa.OpRISCV64CALLtail:
417+
s.TailCall(v)
416418
case ssa.OpRISCV64LoweredWB:
417419
p := s.Prog(obj.ACALL)
418420
p.To.Type = obj.TYPE_MEM
@@ -725,14 +727,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
725727
p.To.Type = obj.TYPE_BRANCH
726728
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
727729
}
728-
case ssa.BlockExit:
730+
case ssa.BlockExit, ssa.BlockRetJmp:
729731
case ssa.BlockRet:
730732
s.Prog(obj.ARET)
731-
case ssa.BlockRetJmp:
732-
p := s.Prog(obj.ARET)
733-
p.To.Type = obj.TYPE_MEM
734-
p.To.Name = obj.NAME_EXTERN
735-
p.To.Sym = b.Aux.(*obj.LSym)
736733
case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ,
737734
ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ,
738735
ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
556556
p.To.Reg = v.Reg()
557557
case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter:
558558
s.Call(v)
559+
case ssa.OpS390XCALLtail:
560+
s.TailCall(v)
559561
case ssa.OpS390XLoweredWB:
560562
p := s.Prog(obj.ACALL)
561563
p.To.Type = obj.TYPE_MEM
@@ -899,17 +901,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
899901
s.Br(s390x.ABR, b.Succs[0].Block())
900902
}
901903
return
902-
case ssa.BlockExit:
904+
case ssa.BlockExit, ssa.BlockRetJmp:
903905
return
904906
case ssa.BlockRet:
905907
s.Prog(obj.ARET)
906908
return
907-
case ssa.BlockRetJmp:
908-
p := s.Prog(s390x.ABR)
909-
p.To.Type = obj.TYPE_MEM
910-
p.To.Name = obj.NAME_EXTERN
911-
p.To.Sym = b.Aux.(*obj.LSym)
912-
return
913909
}
914910

915911
// Handle s390x-specific blocks. These blocks all have a

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,6 @@ func checkFunc(f *Func) {
6666
if !b.Controls[0].Type.IsMemory() {
6767
f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Controls[0].LongString())
6868
}
69-
if b.Aux == nil {
70-
f.Fatalf("retjmp block %s has nil Aux field", b)
71-
}
7269
case BlockPlain:
7370
if len(b.Succs) != 1 {
7471
f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,12 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
10821082
mem := m0
10831083
newArgs := []*Value{}
10841084
oldArgs := []*Value{}
1085+
sp := x.sp
1086+
if v.Op == OpTailLECall {
1087+
// For tail call, we unwind the frame before the call so we'll use the caller's
1088+
// SP.
1089+
sp = x.f.Entry.NewValue0(src.NoXPos, OpGetCallerSP, x.typs.Uintptr)
1090+
}
10851091
for i, a := range v.Args[firstArg : len(v.Args)-1] { // skip leading non-parameter SSA Args and trailing mem SSA Arg.
10861092
oldArgs = append(oldArgs, a)
10871093
auxI := int64(i)
@@ -1094,7 +1100,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
10941100
}
10951101
// "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
10961102
// TODO(register args) this will be more complicated with registers in the picture.
1097-
mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos)
1103+
mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos)
10981104
} else {
10991105
var rc registerCursor
11001106
var result *[]*Value
@@ -1107,7 +1113,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
11071113
if x.debug > 1 {
11081114
x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset)
11091115
}
1110-
rc.init(aRegs, aux.abiInfo, result, x.sp)
1116+
rc.init(aRegs, aux.abiInfo, result, sp)
11111117
mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc)
11121118
}
11131119
}
@@ -1207,7 +1213,7 @@ func expandCalls(f *Func) {
12071213
for _, v := range b.Values {
12081214
firstArg := 0
12091215
switch v.Op {
1210-
case OpStaticLECall:
1216+
case OpStaticLECall, OpTailLECall:
12111217
case OpInterLECall:
12121218
firstArg = 1
12131219
case OpClosureLECall:
@@ -1525,6 +1531,10 @@ func expandCalls(f *Func) {
15251531
v.Op = OpStaticCall
15261532
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
15271533
v.Type = types.NewResults(append(rts, types.TypeMem))
1534+
case OpTailLECall:
1535+
v.Op = OpTailCall
1536+
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
1537+
v.Type = types.NewResults(append(rts, types.TypeMem))
15281538
case OpClosureLECall:
15291539
v.Op = OpClosureCall
15301540
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())

src/cmd/compile/internal/ssa/gen/386.rules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@
317317
(StaticCall ...) => (CALLstatic ...)
318318
(ClosureCall ...) => (CALLclosure ...)
319319
(InterCall ...) => (CALLinter ...)
320+
(TailCall ...) => (CALLtail ...)
320321

321322
// Miscellaneous
322323
(IsNonNil p) => (SETNE (TESTL p p))

0 commit comments

Comments
 (0)