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.
} 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()
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 // TODO: remember this another way?
+ if canSSA(n) {
+ return s.variable(n.Sym.Name, n.Type)
}
- s.argOffsets[n.Sym.Name] = n.Xoffset
- 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:
}
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
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
}
}
+// 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) {
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:
{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
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)
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))