[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>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 773d79b..fd47c54 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -224,8 +224,9 @@
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 @@
} 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 @@
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 @@
}
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) 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 @@
}
}
+// 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 @@
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: