diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 407b143809..8df86b890c 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -726,6 +726,44 @@ func (s *state) stmt(n *Node) { // varkill in the store chain is enough to keep it correctly ordered // with respect to call ops. s.vars[&memvar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, n.Left, s.mem()) + + case OPROC, ODEFER: + call := n.Left + fn := call.Left + if call.Op != OCALLFUNC { + s.Unimplementedf("defer/go of %s", opnames[call.Op]) + } + + // Write argsize and closure (args to Newproc/Deferproc) + argsize := s.constInt32(Types[TUINT32], int32(fn.Type.Argwid)) + s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, 4, s.sp, argsize, s.mem()) + closure := s.expr(fn) + addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(Types[TUINTPTR]), int64(Widthptr), s.sp) + s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), addr, closure, s.mem()) + + // Run all argument assignments. The arg slots have already + // been offset by 2*widthptr. + s.stmtList(call.List) + + // Call deferproc or newproc + bNext := s.f.NewBlock(ssa.BlockPlain) + var op ssa.Op + switch n.Op { + case ODEFER: + op = ssa.OpDeferCall + case OPROC: + op = ssa.OpGoCall + } + r := s.newValue1(op, ssa.TypeMem, s.mem()) + r.AuxInt = fn.Type.Argwid + 2*int64(Widthptr) // total stack space used + s.vars[&memvar] = r + b := s.endBlock() + b.Kind = ssa.BlockCall + b.Control = r + b.AddEdgeTo(bNext) + b.AddEdgeTo(s.exit) + s.startBlock(bNext) + default: s.Unimplementedf("unhandled stmt %s", opnames[n.Op]) } @@ -2494,9 +2532,26 @@ type branch struct { b *ssa.Block // target } +type genState struct { + // branches remembers all the branch instructions we've seen + // and where they would like to go. + branches []branch + + // bstart remembers where each block starts (indexed by block ID) + bstart []*obj.Prog + + // deferBranches remembers all the defer branches we've seen. + deferBranches []*obj.Prog + + // deferTarget remembers the (last) deferreturn call site. + deferTarget *obj.Prog +} + // genssa appends entries to ptxt for each instruction in f. // gcargs and gclocals are filled in with pointer maps for the frame. func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { + var s genState + e := f.Config.Frontend().(*ssaExport) // We're about to emit a bunch of Progs. // Since the only way to get here is to explicitly request it, @@ -2504,11 +2559,7 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { e.mustImplement = true // Remember where each block starts. - bstart := make([]*obj.Prog, f.NumBlocks()) - - // Remember all the branch instructions we've seen - // and where they would like to go - var branches []branch + s.bstart = make([]*obj.Prog, f.NumBlocks()) var valueProgs map[*obj.Prog]*ssa.Value var blockProgs map[*obj.Prog]*ssa.Block @@ -2522,11 +2573,11 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { // Emit basic blocks for i, b := range f.Blocks { - bstart[b.ID] = Pc + s.bstart[b.ID] = Pc // Emit values in block for _, v := range b.Values { x := Pc - genValue(v) + s.genValue(v) if logProgs { for ; x != Pc; x = x.Link { valueProgs[x] = v @@ -2539,7 +2590,7 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { next = f.Blocks[i+1] } x := Pc - branches = genBlock(b, next, branches) + s.genBlock(b, next) if logProgs { for ; x != Pc; x = x.Link { blockProgs[x] = b @@ -2548,8 +2599,11 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { } // Resolve branches - for _, br := range branches { - br.p.To.Val = bstart[br.b.ID] + for _, br := range s.branches { + br.p.To.Val = s.bstart[br.b.ID] + } + for _, p := range s.deferBranches { + p.To.Val = s.deferTarget } Pc.As = obj.ARET // overwrite AEND @@ -2634,7 +2688,7 @@ func opregreg(op int, dest, src int16) *obj.Prog { return p } -func genValue(v *ssa.Value) { +func (s *genState) genValue(v *ssa.Value) { lineno = v.Line switch v.Op { case ssa.OpAMD64ADDQ: @@ -3178,6 +3232,33 @@ func genValue(v *ssa.Value) { if Maxarg < v.AuxInt { Maxarg = v.AuxInt } + case ssa.OpAMD64CALLdefer: + p := Prog(obj.ACALL) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = Linksym(Deferproc.Sym) + if Maxarg < v.AuxInt { + Maxarg = v.AuxInt + } + // defer returns in rax: + // 0 if we should continue executing + // 1 if we should jump to deferreturn call + p = 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 = Prog(x86.AJNE) + p.To.Type = obj.TYPE_BRANCH + s.deferBranches = append(s.deferBranches, p) + case ssa.OpAMD64CALLgo: + p := Prog(obj.ACALL) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = Linksym(Newproc.Sym) + if Maxarg < v.AuxInt { + Maxarg = v.AuxInt + } case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL, ssa.OpAMD64NEGW, ssa.OpAMD64NEGB, ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL, ssa.OpAMD64NOTW, ssa.OpAMD64NOTB: x := regnum(v.Args[0]) @@ -3322,26 +3403,25 @@ func oneFPJump(b *ssa.Block, jumps *floatingEQNEJump, likely ssa.BranchPredictio return branches } -func genFPJump(b, next *ssa.Block, jumps *[2][2]floatingEQNEJump, branches []branch) []branch { +func genFPJump(s *genState, b, next *ssa.Block, jumps *[2][2]floatingEQNEJump) { likely := b.Likely switch next { case b.Succs[0]: - branches = oneFPJump(b, &jumps[0][0], likely, branches) - branches = oneFPJump(b, &jumps[0][1], likely, branches) + s.branches = oneFPJump(b, &jumps[0][0], likely, s.branches) + s.branches = oneFPJump(b, &jumps[0][1], likely, s.branches) case b.Succs[1]: - branches = oneFPJump(b, &jumps[1][0], likely, branches) - branches = oneFPJump(b, &jumps[1][1], likely, branches) + s.branches = oneFPJump(b, &jumps[1][0], likely, s.branches) + s.branches = oneFPJump(b, &jumps[1][1], likely, s.branches) default: - branches = oneFPJump(b, &jumps[1][0], likely, branches) - branches = oneFPJump(b, &jumps[1][1], likely, branches) + s.branches = oneFPJump(b, &jumps[1][0], likely, s.branches) + s.branches = oneFPJump(b, &jumps[1][1], likely, s.branches) q := Prog(obj.AJMP) q.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{q, b.Succs[1]}) + s.branches = append(s.branches, branch{q, b.Succs[1]}) } - return branches } -func genBlock(b, next *ssa.Block, branches []branch) []branch { +func (s *genState) genBlock(b, next *ssa.Block) { lineno = b.Line // after a panic call, don't emit any branch code @@ -3350,7 +3430,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { case ssa.OpAMD64LoweredPanicNilCheck, ssa.OpAMD64LoweredPanicIndexCheck, ssa.OpAMD64LoweredPanicSliceCheck: - return branches + return } } @@ -3359,23 +3439,39 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { if b.Succs[0] != next { p := Prog(obj.AJMP) p.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{p, b.Succs[0]}) + s.branches = append(s.branches, branch{p, b.Succs[0]}) } case ssa.BlockExit: case ssa.BlockRet: + if Hasdefer != 0 { + // Deferred calls will appear to be returning to + // the CALL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction byte before the return PC. + // To avoid that being an unrelated instruction, + // insert an actual hardware NOP that will have the right line number. + // This is different from obj.ANOP, which is a virtual no-op + // that doesn't make it into the instruction stream. + s.deferTarget = Pc + Thearch.Ginsnop() + p := Prog(obj.ACALL) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = Linksym(Deferreturn.Sym) + } Prog(obj.ARET) case ssa.BlockCall: if b.Succs[0] != next { p := Prog(obj.AJMP) p.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{p, b.Succs[0]}) + s.branches = append(s.branches, branch{p, b.Succs[0]}) } case ssa.BlockAMD64EQF: - branches = genFPJump(b, next, &eqfJumps, branches) + genFPJump(s, b, next, &eqfJumps) case ssa.BlockAMD64NEF: - branches = genFPJump(b, next, &nefJumps, branches) + genFPJump(s, b, next, &nefJumps) case ssa.BlockAMD64EQ, ssa.BlockAMD64NE, ssa.BlockAMD64LT, ssa.BlockAMD64GE, @@ -3390,18 +3486,18 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { p = Prog(jmp.invasm) likely *= -1 p.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{p, b.Succs[1]}) + s.branches = append(s.branches, branch{p, b.Succs[1]}) case b.Succs[1]: p = Prog(jmp.asm) p.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{p, b.Succs[0]}) + s.branches = append(s.branches, branch{p, b.Succs[0]}) default: p = Prog(jmp.asm) p.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{p, b.Succs[0]}) + s.branches = append(s.branches, branch{p, b.Succs[0]}) q := Prog(obj.AJMP) q.To.Type = obj.TYPE_BRANCH - branches = append(branches, branch{q, b.Succs[1]}) + s.branches = append(s.branches, branch{q, b.Succs[1]}) } // liblink reorders the instruction stream as it sees fit. @@ -3420,7 +3516,6 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { default: b.Unimplementedf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString()) } - return branches } // addAux adds the offset in the aux fields (AuxInt and Aux) of v to a. diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 16bd1df84b..cba16eadc7 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -352,6 +352,8 @@ (StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem) (ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem) +(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem) +(GoCall [argwid] mem) -> (CALLgo [argwid] mem) // Rules below here apply some simple optimizations after lowering. // TODO: Should this be a separate pass? diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 37cd096d63..0eee551f32 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -386,6 +386,8 @@ func init() { //TODO: set register clobber to everything? {name: "CALLstatic", reg: regInfo{clobbers: callerSave}}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, callerSave, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLdefer", reg: regInfo{clobbers: callerSave}}, // call deferproc. arg0=mem, auxint=argsize, returns mem + {name: "CALLgo", reg: regInfo{clobbers: callerSave}}, // call newproc. arg0=mem, auxint=argsize, returns mem {name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 81fe20547e..b52bd1fecc 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -280,6 +280,8 @@ var genericOps = []opData{ // as a phantom first argument. {name: "ClosureCall"}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. {name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. auxint=arg size. Returns memory. + {name: "DeferCall"}, // defer call. arg0=memory, auxint=arg size. Returns memory. + {name: "GoCall"}, // go call. arg0=memory, auxint=arg size. Returns memory. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", typ: "Int16"}, diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 087a0e75b8..0d7343c8aa 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -261,6 +261,8 @@ const ( OpAMD64REPSTOSQ OpAMD64CALLstatic OpAMD64CALLclosure + OpAMD64CALLdefer + OpAMD64CALLgo OpAMD64REPMOVSB OpAMD64InvertFlags OpAMD64LoweredPanicNilCheck @@ -469,6 +471,8 @@ const ( OpZero OpClosureCall OpStaticCall + OpDeferCall + OpGoCall OpSignExt8to16 OpSignExt8to32 OpSignExt8to64 @@ -3047,6 +3051,18 @@ var opcodeTable = [...]opInfo{ clobbers: 12884901871, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 .FLAGS }, }, + { + name: "CALLdefer", + reg: regInfo{ + clobbers: 12884901871, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 .FLAGS + }, + }, + { + name: "CALLgo", + reg: regInfo{ + clobbers: 12884901871, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 .FLAGS + }, + }, { name: "REPMOVSB", reg: regInfo{ @@ -3891,6 +3907,14 @@ var opcodeTable = [...]opInfo{ name: "StaticCall", generic: true, }, + { + name: "DeferCall", + generic: true, + }, + { + name: "GoCall", + generic: true, + }, { name: "SignExt8to16", generic: true, diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 8ad939ead9..7917d8d971 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -1830,6 +1830,24 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endf74ce5df659f385f75c61187b515a5d0 endf74ce5df659f385f75c61187b515a5d0: ; + case OpDeferCall: + // match: (DeferCall [argwid] mem) + // cond: + // result: (CALLdefer [argwid] mem) + { + argwid := v.AuxInt + mem := v.Args[0] + v.Op = OpAMD64CALLdefer + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AuxInt = argwid + v.AddArg(mem) + return true + } + goto end1c408581037450df959dd1fb7554a022 + end1c408581037450df959dd1fb7554a022: + ; case OpDiv16: // match: (Div16 x y) // cond: @@ -2393,6 +2411,24 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endb17140e71dd641aa4d89e14479160260 endb17140e71dd641aa4d89e14479160260: ; + case OpGoCall: + // match: (GoCall [argwid] mem) + // cond: + // result: (CALLgo [argwid] mem) + { + argwid := v.AuxInt + mem := v.Args[0] + v.Op = OpAMD64CALLgo + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AuxInt = argwid + v.AddArg(mem) + return true + } + goto end1cef0f92c46e6aaa2c7abdf5f2794baf + end1cef0f92c46e6aaa2c7abdf5f2794baf: + ; case OpGreater16: // match: (Greater16 x y) // cond: