mirror of
https://github.com/golang/go
synced 2024-10-05 22:21:23 -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:
parent
81ccf508aa
commit
290d8fc14a
@ -33,6 +33,7 @@ var opnames = []string{
|
|||||||
OAS2MAPR: "AS2MAPR",
|
OAS2MAPR: "AS2MAPR",
|
||||||
OAS2DOTTYPE: "AS2DOTTYPE",
|
OAS2DOTTYPE: "AS2DOTTYPE",
|
||||||
OASOP: "ASOP",
|
OASOP: "ASOP",
|
||||||
|
OASWB: "ASWB",
|
||||||
OCALL: "CALL",
|
OCALL: "CALL",
|
||||||
OCALLFUNC: "CALLFUNC",
|
OCALLFUNC: "CALLFUNC",
|
||||||
OCALLMETH: "CALLMETH",
|
OCALLMETH: "CALLMETH",
|
||||||
|
@ -224,8 +224,9 @@ func (s *state) stmt(n *Node) {
|
|||||||
s.startBlock(t)
|
s.startBlock(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
case OAS:
|
case OAS, OASWB:
|
||||||
// TODO(khr): colas?
|
// TODO(khr): colas?
|
||||||
|
// TODO: do write barrier
|
||||||
var val *ssa.Value
|
var val *ssa.Value
|
||||||
if n.Right == nil {
|
if n.Right == nil {
|
||||||
// n.Right == nil means use the zero value of the assigned type.
|
// n.Right == nil means use the zero value of the assigned type.
|
||||||
@ -243,15 +244,14 @@ func (s *state) stmt(n *Node) {
|
|||||||
} else {
|
} else {
|
||||||
val = s.expr(n.Right)
|
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 {
|
if n.Left.Op == ONAME && canSSA(n.Left) {
|
||||||
// ssa-able variable.
|
// Update variable assignment.
|
||||||
s.vars[n.Left.Sym.Name] = val
|
s.vars[n.Left.Sym.Name] = val
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// not ssa-able. Treat as a store.
|
// not ssa-able. Treat as a store.
|
||||||
addr := s.addr(n.Left)
|
addr := s.addr(n.Left)
|
||||||
s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
|
s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
|
||||||
// TODO: try to make more variables registerizeable.
|
|
||||||
case OIF:
|
case OIF:
|
||||||
cond := s.expr(n.Ntest)
|
cond := s.expr(n.Ntest)
|
||||||
b := s.endBlock()
|
b := s.endBlock()
|
||||||
@ -338,14 +338,16 @@ func (s *state) expr(n *Node) *ssa.Value {
|
|||||||
|
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
case ONAME:
|
case ONAME:
|
||||||
// TODO: remember offsets for PPARAM names
|
if n.Class == PFUNC {
|
||||||
if n.Class == PEXTERN {
|
// "value" of a function is the address of the function's closure
|
||||||
// global variable
|
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym))
|
||||||
addr := s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym)
|
|
||||||
return s.newValue2(ssa.OpLoad, n.Type, nil, addr, s.mem())
|
|
||||||
}
|
}
|
||||||
s.argOffsets[n.Sym.Name] = n.Xoffset
|
s.argOffsets[n.Sym.Name] = n.Xoffset // TODO: remember this another way?
|
||||||
|
if canSSA(n) {
|
||||||
return s.variable(n.Sym.Name, n.Type)
|
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:
|
case OLITERAL:
|
||||||
switch n.Val.Ctype {
|
switch n.Val.Ctype {
|
||||||
case CTINT:
|
case CTINT:
|
||||||
@ -415,17 +417,25 @@ func (s *state) expr(n *Node) *ssa.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case OCALLFUNC:
|
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
|
// 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)
|
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)
|
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 := s.endBlock()
|
||||||
b.Kind = ssa.BlockCall
|
b.Kind = ssa.BlockCall
|
||||||
b.Control = call
|
b.Control = call
|
||||||
@ -448,17 +458,18 @@ func (s *state) expr(n *Node) *ssa.Value {
|
|||||||
func (s *state) addr(n *Node) *ssa.Value {
|
func (s *state) addr(n *Node) *ssa.Value {
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
case ONAME:
|
case ONAME:
|
||||||
if n.Class == PEXTERN {
|
switch n.Class {
|
||||||
|
case PEXTERN:
|
||||||
// global variable
|
// global variable
|
||||||
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym)
|
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym)
|
||||||
}
|
case PPARAMOUT:
|
||||||
if n.Class == PPARAMOUT {
|
|
||||||
// store to parameter slot
|
// store to parameter slot
|
||||||
return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp)
|
return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp)
|
||||||
}
|
default:
|
||||||
// TODO: address of locals
|
// TODO: address of locals
|
||||||
log.Fatalf("variable address of %v not implemented", n)
|
log.Fatalf("variable address of %v not implemented", n)
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
case OINDREG:
|
case OINDREG:
|
||||||
// indirect off a register (TODO: always SP?)
|
// indirect off a register (TODO: always SP?)
|
||||||
// used for storing/loading arguments/returns to/from callees
|
// 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.
|
// nilCheck generates nil pointer checking code.
|
||||||
// Starts a new block on return.
|
// Starts a new block on return.
|
||||||
func (s *state) nilCheck(ptr *ssa.Value) {
|
func (s *state) nilCheck(ptr *ssa.Value) {
|
||||||
@ -854,11 +887,15 @@ func genValue(v *ssa.Value) {
|
|||||||
p.From.Offset = g.Offset
|
p.From.Offset = g.Offset
|
||||||
p.To.Type = obj.TYPE_REG
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Reg = regnum(v)
|
p.To.Reg = regnum(v)
|
||||||
case ssa.OpStaticCall:
|
case ssa.OpAMD64CALLstatic:
|
||||||
p := Prog(obj.ACALL)
|
p := Prog(obj.ACALL)
|
||||||
p.To.Type = obj.TYPE_MEM
|
p.To.Type = obj.TYPE_MEM
|
||||||
p.To.Name = obj.NAME_EXTERN
|
p.To.Name = obj.NAME_EXTERN
|
||||||
p.To.Sym = Linksym(v.Aux.(*Sym))
|
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:
|
case ssa.OpFP, ssa.OpSP:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
default:
|
default:
|
||||||
|
@ -26,6 +26,7 @@ Opcodes
|
|||||||
- It's annoying to list the opcode both in the opcode list and an
|
- 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
|
opInfo map entry. Specify it one place and use go:generate to
|
||||||
produce both?
|
produce both?
|
||||||
|
- Write barriers
|
||||||
|
|
||||||
Regalloc
|
Regalloc
|
||||||
- Make less arch-dependent
|
- Make less arch-dependent
|
||||||
@ -33,6 +34,7 @@ Regalloc
|
|||||||
- Allow args and return values to be ssa-able.
|
- Allow args and return values to be ssa-able.
|
||||||
- Handle 2-address instructions.
|
- Handle 2-address instructions.
|
||||||
- Floating point registers
|
- Floating point registers
|
||||||
|
- Make calls clobber all registers
|
||||||
|
|
||||||
Rewrites
|
Rewrites
|
||||||
- Strength reduction (both arch-indep and arch-dependent?)
|
- Strength reduction (both arch-indep and arch-dependent?)
|
||||||
@ -51,3 +53,4 @@ Common-Subexpression Elimination
|
|||||||
Other
|
Other
|
||||||
- Make go:generate less painful. Have a subpackage that just has the
|
- Make go:generate less painful. Have a subpackage that just has the
|
||||||
generate commands in it?
|
generate commands in it?
|
||||||
|
- Use gc.Fatal for errors. Add a callback to Frontend?
|
||||||
|
@ -40,6 +40,9 @@
|
|||||||
(If (SETB cmp) yes no) -> (ULT cmp yes no)
|
(If (SETB cmp) yes no) -> (ULT cmp yes no)
|
||||||
(If cond yes no) && cond.Op == OpAMD64MOVBload -> (NE (TESTB <TypeFlags> cond cond) 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.
|
// Rules below here apply some simple optimizations after lowering.
|
||||||
// TODO: Should this be a separate pass?
|
// TODO: Should this be a separate pass?
|
||||||
|
|
||||||
|
@ -132,6 +132,10 @@ func init() {
|
|||||||
{name: "MOVQloadglobal"}, // Load from aux.(GlobalOffset). arg0 = memory
|
{name: "MOVQloadglobal"}, // Load from aux.(GlobalOffset). arg0 = memory
|
||||||
{name: "MOVQstoreglobal"}, // store arg0 to aux.(GlobalOffset). arg1=memory, returns 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: "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
|
{name: "ADDL", reg: gp21}, // arg0+arg1
|
||||||
|
@ -44,7 +44,7 @@ var genericOps = []opData{
|
|||||||
// Function calls. Arguments to the call have already been written to the stack.
|
// 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
|
// Return values appear on the stack. The method receiver, if any, is treated
|
||||||
// as a phantom first argument.
|
// as a phantom first argument.
|
||||||
{name: "Call"}, // arg0=code pointer, arg1=context ptr, arg2=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.
|
{name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. Returns memory.
|
||||||
|
|
||||||
// Conversions
|
// Conversions
|
||||||
|
@ -82,6 +82,8 @@ const (
|
|||||||
OpAMD64MOVQstoreidx8
|
OpAMD64MOVQstoreidx8
|
||||||
OpAMD64MOVQloadglobal
|
OpAMD64MOVQloadglobal
|
||||||
OpAMD64MOVQstoreglobal
|
OpAMD64MOVQstoreglobal
|
||||||
|
OpAMD64CALLstatic
|
||||||
|
OpAMD64CALLclosure
|
||||||
OpAMD64REPMOVSB
|
OpAMD64REPMOVSB
|
||||||
OpAMD64ADDL
|
OpAMD64ADDL
|
||||||
OpAMD64InvertFlags
|
OpAMD64InvertFlags
|
||||||
@ -103,7 +105,7 @@ const (
|
|||||||
OpLoad
|
OpLoad
|
||||||
OpStore
|
OpStore
|
||||||
OpMove
|
OpMove
|
||||||
OpCall
|
OpClosureCall
|
||||||
OpStaticCall
|
OpStaticCall
|
||||||
OpConvert
|
OpConvert
|
||||||
OpConvNop
|
OpConvNop
|
||||||
@ -553,6 +555,26 @@ var opcodeTable = [...]opInfo{
|
|||||||
outputs: []regMask{},
|
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",
|
name: "REPMOVSB",
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
@ -741,7 +763,7 @@ var opcodeTable = [...]opInfo{
|
|||||||
generic: true,
|
generic: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Call",
|
name: "ClosureCall",
|
||||||
reg: regInfo{
|
reg: regInfo{
|
||||||
inputs: []regMask{},
|
inputs: []regMask{},
|
||||||
clobbers: 0,
|
clobbers: 0,
|
||||||
|
@ -191,6 +191,25 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
|
|||||||
goto endf8ca12fe79290bc82b11cfa463bc9413
|
goto endf8ca12fe79290bc82b11cfa463bc9413
|
||||||
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:
|
case OpConst:
|
||||||
// match: (Const <t> [val])
|
// match: (Const <t> [val])
|
||||||
// cond: is64BitInt(t)
|
// cond: is64BitInt(t)
|
||||||
@ -743,6 +762,23 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
|
|||||||
goto end78e66b6fc298684ff4ac8aec5ce873c9
|
goto end78e66b6fc298684ff4ac8aec5ce873c9
|
||||||
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:
|
case OpStore:
|
||||||
// match: (Store ptr val mem)
|
// match: (Store ptr val mem)
|
||||||
// cond: (is64BitInt(val.Type) || isPtr(val.Type))
|
// cond: (is64BitInt(val.Type) || isPtr(val.Type))
|
||||||
|
Loading…
Reference in New Issue
Block a user