1
0
mirror of https://github.com/golang/go synced 2024-10-05 16:31:21 -06:00

[dev.ssa] cmd/compile/internal/gc: implement go and defer

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>
This commit is contained in:
Keith Randall 2015-08-28 22:51:01 -07:00
parent 6b9b618787
commit 9569b957cb
6 changed files with 192 additions and 31 deletions

View 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.

View File

@ -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?

View 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

View 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"},

View 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,

View 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: