diff --git a/src/cmd/compile/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go index fc03ec6666..9134bd4332 100644 --- a/src/cmd/compile/internal/gc/opnames.go +++ b/src/cmd/compile/internal/gc/opnames.go @@ -33,6 +33,7 @@ var opnames = []string{ OAS2MAPR: "AS2MAPR", OAS2DOTTYPE: "AS2DOTTYPE", OASOP: "ASOP", + OASWB: "ASWB", OCALL: "CALL", OCALLFUNC: "CALLFUNC", OCALLMETH: "CALLMETH", diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 773d79ba30..fd47c54ad0 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -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: diff --git a/src/cmd/compile/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO index 7cd2206db3..d5e8788e36 100644 --- a/src/cmd/compile/internal/ssa/TODO +++ b/src/cmd/compile/internal/ssa/TODO @@ -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? diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index c4ff744421..15cd79a3f5 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -40,6 +40,9 @@ (If (SETB cmp) yes no) -> (ULT cmp yes no) (If cond yes no) && cond.Op == OpAMD64MOVBload -> (NE (TESTB 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? diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 38d1e87575..b3b2e3b5e2 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -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 diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index e8c3cbeb8a..e415f3d16b 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -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 diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 604f096152..550f641c14 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -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, diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 0878a12eb9..542dad4500 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -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 [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))