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:
parent
81ccf508aa
commit
290d8fc14a
@ -33,6 +33,7 @@ var opnames = []string{
|
||||
OAS2MAPR: "AS2MAPR",
|
||||
OAS2DOTTYPE: "AS2DOTTYPE",
|
||||
OASOP: "ASOP",
|
||||
OASWB: "ASWB",
|
||||
OCALL: "CALL",
|
||||
OCALLFUNC: "CALLFUNC",
|
||||
OCALLMETH: "CALLMETH",
|
||||
|
@ -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:
|
||||
|
@ -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?
|
||||
|
@ -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?
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user