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

[dev.ssa] cmd/compile/internal/ssa: add call opcodes

Add calls, particularly closure calls.

Reorg SSAable variable test for converting to SSA.

Change-Id: Ia75c04295e6b0b040122f97e2381836a393b7f42
Reviewed-on: https://go-review.googlesource.com/10912
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
Keith Randall 2015-06-10 15:03:06 -07:00
parent 81ccf508aa
commit 290d8fc14a
8 changed files with 135 additions and 29 deletions

View File

@ -33,6 +33,7 @@ var opnames = []string{
OAS2MAPR: "AS2MAPR",
OAS2DOTTYPE: "AS2DOTTYPE",
OASOP: "ASOP",
OASWB: "ASWB",
OCALL: "CALL",
OCALLFUNC: "CALLFUNC",
OCALLMETH: "CALLMETH",

View File

@ -224,8 +224,9 @@ func (s *state) stmt(n *Node) {
s.startBlock(t)
}
case OAS:
case OAS, OASWB:
// TODO(khr): colas?
// TODO: do write barrier
var val *ssa.Value
if n.Right == nil {
// n.Right == nil means use the zero value of the assigned type.
@ -243,15 +244,14 @@ func (s *state) stmt(n *Node) {
} else {
val = s.expr(n.Right)
}
if n.Left.Op == ONAME && !n.Left.Addrtaken && n.Left.Class&PHEAP == 0 && n.Left.Class != PEXTERN && n.Left.Class != PPARAMOUT {
// ssa-able variable.
if n.Left.Op == ONAME && canSSA(n.Left) {
// Update variable assignment.
s.vars[n.Left.Sym.Name] = val
return
}
// not ssa-able. Treat as a store.
addr := s.addr(n.Left)
s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
// TODO: try to make more variables registerizeable.
case OIF:
cond := s.expr(n.Ntest)
b := s.endBlock()
@ -338,14 +338,16 @@ func (s *state) expr(n *Node) *ssa.Value {
switch n.Op {
case ONAME:
// TODO: remember offsets for PPARAM names
if n.Class == PEXTERN {
// global variable
addr := s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym)
return s.newValue2(ssa.OpLoad, n.Type, nil, addr, s.mem())
if n.Class == PFUNC {
// "value" of a function is the address of the function's closure
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym))
}
s.argOffsets[n.Sym.Name] = n.Xoffset
return s.variable(n.Sym.Name, n.Type)
s.argOffsets[n.Sym.Name] = n.Xoffset // TODO: remember this another way?
if canSSA(n) {
return s.variable(n.Sym.Name, n.Type)
}
addr := s.addr(n)
return s.newValue2(ssa.OpLoad, n.Type, nil, addr, s.mem())
case OLITERAL:
switch n.Val.Ctype {
case CTINT:
@ -415,17 +417,25 @@ func (s *state) expr(n *Node) *ssa.Value {
}
case OCALLFUNC:
static := n.Left.Op == ONAME && n.Left.Class == PFUNC
// evaluate closure
var closure *ssa.Value
if !static {
closure = s.expr(n.Left)
}
// run all argument assignments
// TODO(khr): do we need to evaluate function first?
// Or is it already side-effect-free and does not require a call?
s.stmtList(n.List)
if n.Left.Op != ONAME {
// TODO(khr): closure calls?
log.Fatalf("can't handle CALLFUNC with non-ONAME fn %s", opnames[n.Left.Op])
}
bNext := s.f.NewBlock(ssa.BlockPlain)
call := s.newValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem())
var call *ssa.Value
if static {
call = s.newValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem())
} else {
entry := s.newValue2(ssa.OpLoad, s.config.Uintptr, nil, closure, s.mem())
call = s.newValue3(ssa.OpClosureCall, ssa.TypeMem, nil, entry, closure, s.mem())
}
b := s.endBlock()
b.Kind = ssa.BlockCall
b.Control = call
@ -448,17 +458,18 @@ func (s *state) expr(n *Node) *ssa.Value {
func (s *state) addr(n *Node) *ssa.Value {
switch n.Op {
case ONAME:
if n.Class == PEXTERN {
switch n.Class {
case PEXTERN:
// global variable
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym)
}
if n.Class == PPARAMOUT {
case PPARAMOUT:
// store to parameter slot
return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp)
default:
// TODO: address of locals
log.Fatalf("variable address of %v not implemented", n)
return nil
}
// TODO: address of locals
log.Fatalf("variable address of %v not implemented", n)
return nil
case OINDREG:
// indirect off a register (TODO: always SP?)
// used for storing/loading arguments/returns to/from callees
@ -484,6 +495,28 @@ func (s *state) addr(n *Node) *ssa.Value {
}
}
// canSSA reports whether n is SSA-able.
// n must be an ONAME.
func canSSA(n *Node) bool {
if n.Op != ONAME {
log.Fatalf("canSSA passed a non-ONAME %s %v", Oconv(int(n.Op), 0), n)
}
if n.Addrtaken {
return false
}
if n.Class&PHEAP != 0 {
return false
}
if n.Class == PEXTERN {
return false
}
if n.Class == PPARAMOUT {
return false
}
return true
// TODO: try to make more variables SSAable.
}
// nilCheck generates nil pointer checking code.
// Starts a new block on return.
func (s *state) nilCheck(ptr *ssa.Value) {
@ -854,11 +887,15 @@ func genValue(v *ssa.Value) {
p.From.Offset = g.Offset
p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v)
case ssa.OpStaticCall:
case ssa.OpAMD64CALLstatic:
p := Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = Linksym(v.Aux.(*Sym))
case ssa.OpAMD64CALLclosure:
p := Prog(obj.ACALL)
p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v.Args[0])
case ssa.OpFP, ssa.OpSP:
// nothing to do
default:

View File

@ -26,6 +26,7 @@ Opcodes
- It's annoying to list the opcode both in the opcode list and an
opInfo map entry. Specify it one place and use go:generate to
produce both?
- Write barriers
Regalloc
- Make less arch-dependent
@ -33,6 +34,7 @@ Regalloc
- Allow args and return values to be ssa-able.
- Handle 2-address instructions.
- Floating point registers
- Make calls clobber all registers
Rewrites
- Strength reduction (both arch-indep and arch-dependent?)
@ -51,3 +53,4 @@ Common-Subexpression Elimination
Other
- Make go:generate less painful. Have a subpackage that just has the
generate commands in it?
- Use gc.Fatal for errors. Add a callback to Frontend?

View File

@ -40,6 +40,9 @@
(If (SETB cmp) yes no) -> (ULT cmp yes no)
(If cond yes no) && cond.Op == OpAMD64MOVBload -> (NE (TESTB <TypeFlags> cond cond) yes no)
(StaticCall [target] mem) -> (CALLstatic [target] mem)
(ClosureCall entry closure mem) -> (CALLclosure entry closure mem)
// Rules below here apply some simple optimizations after lowering.
// TODO: Should this be a separate pass?

View File

@ -132,6 +132,10 @@ func init() {
{name: "MOVQloadglobal"}, // Load from aux.(GlobalOffset). arg0 = memory
{name: "MOVQstoreglobal"}, // store arg0 to aux.(GlobalOffset). arg1=memory, returns memory.
//TODO: set register clobber to everything?
{name: "CALLstatic"}, // call static function. arg0=mem, returns mem
{name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, 0, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem 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
{name: "ADDL", reg: gp21}, // arg0+arg1

View File

@ -44,8 +44,8 @@ var genericOps = []opData{
// Function calls. Arguments to the call have already been written to the stack.
// Return values appear on the stack. The method receiver, if any, is treated
// as a phantom first argument.
{name: "Call"}, // arg0=code pointer, arg1=context ptr, arg2=memory. Returns memory.
{name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. Returns memory.
{name: "ClosureCall"}, // arg0=code pointer, arg1=context ptr, arg2=memory. Returns memory.
{name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. Returns memory.
// Conversions
{name: "Convert"}, // convert arg0 to another type

View File

@ -82,6 +82,8 @@ const (
OpAMD64MOVQstoreidx8
OpAMD64MOVQloadglobal
OpAMD64MOVQstoreglobal
OpAMD64CALLstatic
OpAMD64CALLclosure
OpAMD64REPMOVSB
OpAMD64ADDL
OpAMD64InvertFlags
@ -103,7 +105,7 @@ const (
OpLoad
OpStore
OpMove
OpCall
OpClosureCall
OpStaticCall
OpConvert
OpConvNop
@ -553,6 +555,26 @@ var opcodeTable = [...]opInfo{
outputs: []regMask{},
},
},
{
name: "CALLstatic",
reg: regInfo{
inputs: []regMask{},
clobbers: 0,
outputs: []regMask{},
},
},
{
name: "CALLclosure",
reg: regInfo{
inputs: []regMask{
4295032831,
4,
0,
},
clobbers: 0,
outputs: []regMask{},
},
},
{
name: "REPMOVSB",
reg: regInfo{
@ -741,7 +763,7 @@ var opcodeTable = [...]opInfo{
generic: true,
},
{
name: "Call",
name: "ClosureCall",
reg: regInfo{
inputs: []regMask{},
clobbers: 0,

View File

@ -191,6 +191,25 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
goto endf8ca12fe79290bc82b11cfa463bc9413
endf8ca12fe79290bc82b11cfa463bc9413:
;
case OpClosureCall:
// match: (ClosureCall entry closure mem)
// cond:
// result: (CALLclosure entry closure mem)
{
entry := v.Args[0]
closure := v.Args[1]
mem := v.Args[2]
v.Op = OpAMD64CALLclosure
v.Aux = nil
v.resetArgs()
v.AddArg(entry)
v.AddArg(closure)
v.AddArg(mem)
return true
}
goto endee26da781e813a3c602ccb4f7ade98c7
endee26da781e813a3c602ccb4f7ade98c7:
;
case OpConst:
// match: (Const <t> [val])
// cond: is64BitInt(t)
@ -743,6 +762,23 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
goto end78e66b6fc298684ff4ac8aec5ce873c9
end78e66b6fc298684ff4ac8aec5ce873c9:
;
case OpStaticCall:
// match: (StaticCall [target] mem)
// cond:
// result: (CALLstatic [target] mem)
{
target := v.Aux
mem := v.Args[0]
v.Op = OpAMD64CALLstatic
v.Aux = nil
v.resetArgs()
v.Aux = target
v.AddArg(mem)
return true
}
goto endcf02eb60d90086f6c42bfdc5842b145d
endcf02eb60d90086f6c42bfdc5842b145d:
;
case OpStore:
// match: (Store ptr val mem)
// cond: (is64BitInt(val.Type) || isPtr(val.Type))