]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile/internal/gc: implement go and defer
authorKeith Randall <khr@golang.org>
Sat, 29 Aug 2015 05:51:01 +0000 (22:51 -0700)
committerKeith Randall <khr@golang.org>
Sun, 6 Sep 2015 02:42:24 +0000 (02:42 +0000)
TODO: for now, just function calls.  Do method and interface calls.

Change-Id: Ib262dfa31cb753996cde899beaad4dc2e66705ac
Reviewed-on: https://go-review.googlesource.com/14035
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/gen/AMD64.rules
src/cmd/compile/internal/ssa/gen/AMD64Ops.go
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/rewriteAMD64.go

index 407b143809ae5f137a4394daaf9306fd84600acb..8df86b890cc3f7124826f9fb2dacdf0d7cb648cc 100644 (file)
@@ -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.
index 16bd1df84b6b4b54023ae7b6276e83cd3b782a40..cba16eadc7570eb924fa424697bb6407102446b3 100644 (file)
 
 (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?
index 37cd096d63fc5e1267266a546e53edf136c4d2cc..0eee551f32e3185470ae0b4ce9b2c4bf346b55ba 100644 (file)
@@ -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
 
index 81fe20547e2f4c7d240bacf131eaf989f2a949ea..b52bd1fecc7751676d75cfec23cf08f2836771cc 100644 (file)
@@ -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"},
index 087a0e75b89e74146e6b68c4b2854e94fb318988..0d7343c8aaeeb3d488a000ef8d3a3f18b11b65d8 100644 (file)
@@ -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,
index 8ad939ead97dcd75bdecee3af4ea3e817580cd2e..7917d8d97119811cbdcb05d2cb0828095c6384d7 100644 (file)
@@ -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: