"byte %c": "",
"cmd/compile/internal/arm.shift %d": "",
"cmd/compile/internal/gc.Class %d": "",
+ "cmd/compile/internal/gc.Class %s": "",
"cmd/compile/internal/gc.Class %v": "",
"cmd/compile/internal/gc.Ctype %d": "",
"cmd/compile/internal/gc.Ctype %v": "",
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
- case *ssa.ExternSymbol:
+ case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
- case *ssa.ArgSymbol, *ssa.AutoSymbol:
+ case *gc.Node:
wantreg = "SP"
gc.AddAux(&p.From, v)
case nil:
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
- case *ssa.ExternSymbol:
+ case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
- case *ssa.ArgSymbol, *ssa.AutoSymbol:
+ case *gc.Node:
wantreg = "SP"
gc.AddAux(&p.From, v)
case nil:
scratchUsed := false
for _, b := range f.Blocks {
for _, v := range b.Values {
- switch a := v.Aux.(type) {
- case *ssa.ArgSymbol:
- n := a.Node.(*Node)
- // Don't modify nodfp; it is a global.
- if n != nodfp {
+ if n, ok := v.Aux.(*Node); ok {
+ switch n.Class() {
+ case PPARAM, PPARAMOUT:
+ // Don't modify nodfp; it is a global.
+ if n != nodfp {
+ n.Name.SetUsed(true)
+ }
+ case PAUTO:
n.Name.SetUsed(true)
}
- case *ssa.AutoSymbol:
- a.Node.(*Node).Name.SetUsed(true)
}
-
if !scratchUsed {
scratchUsed = v.Op.UsesScratch()
}
+
}
}
return n, ssa.SymWrite
case ssa.OpVarLive:
- switch a := v.Aux.(type) {
- case *ssa.ArgSymbol:
- return a.Node.(*Node), ssa.SymRead
- case *ssa.AutoSymbol:
- return a.Node.(*Node), ssa.SymRead
- default:
- Fatalf("unknown VarLive aux type: %s", v.LongString())
- }
+ return v.Aux.(*Node), ssa.SymRead
case ssa.OpVarDef, ssa.OpVarKill:
return v.Aux.(*Node), ssa.SymWrite
case ssa.OpKeepAlive:
var n *Node
switch a := v.Aux.(type) {
- case nil, *ssa.ExternSymbol:
+ case nil, *obj.LSym:
// ok, but no node
- case *ssa.ArgSymbol:
- n = a.Node.(*Node)
- case *ssa.AutoSymbol:
- n = a.Node.(*Node)
+ case *Node:
+ n = a
default:
Fatalf("weird aux: %s", v.LongString())
}
// clobberPtr generates a clobber of the pointer at offset offset in v.
// The clobber instruction is added at the end of b.
func clobberPtr(b *ssa.Block, v *Node, offset int64) {
- var aux interface{}
- if v.Class() == PAUTO {
- aux = &ssa.AutoSymbol{Node: v}
- } else {
- aux = &ssa.ArgSymbol{Node: v}
- }
- b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, aux)
+ b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v)
}
func (lv *Liveness) avarinitanyall(b *ssa.Block, any, all bvec) {
s.startBlock(s.f.Entry)
s.vars[&memVar] = s.startmem
- s.varsyms = map[*Node]interface{}{}
-
// Generate addresses of local declarations
s.decladdrs = map[*Node]*ssa.Value{}
for _, n := range fn.Func.Dcl {
switch n.Class() {
case PPARAM, PPARAMOUT:
- aux := s.lookupSymbol(n, &ssa.ArgSymbol{Node: n})
- s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type), aux, s.sp)
+ s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type), n, s.sp)
if n.Class() == PPARAMOUT && s.canSSA(n) {
// Save ssa-able PPARAMOUT variables so we can
// store them back to the stack at the end of
// addresses of PPARAM and PPARAMOUT variables.
decladdrs map[*Node]*ssa.Value
- // symbols for PEXTERN, PAUTO and PPARAMOUT variables so they can be reused.
- varsyms map[*Node]interface{}
-
// starting values. Memory, stack pointer, and globals pointer
startmem *ssa.Value
sp *ssa.Value
if !n.Left.Addrtaken() {
s.Fatalf("VARLIVE variable %v must have Addrtaken set", n.Left)
}
- var aux interface{}
switch n.Left.Class() {
- case PAUTO:
- aux = s.lookupSymbol(n.Left, &ssa.AutoSymbol{Node: n.Left})
- case PPARAM, PPARAMOUT:
- aux = s.lookupSymbol(n.Left, &ssa.ArgSymbol{Node: n.Left})
+ case PAUTO, PPARAM, PPARAMOUT:
default:
s.Fatalf("VARLIVE variable %v must be Auto or Arg", n.Left)
}
- s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, aux, s.mem())
+ s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, n.Left, s.mem())
case OCHECKNIL:
p := s.expr(n.Left)
len := s.newValue1(ssa.OpStringLen, types.Types[TINT], str)
return s.newValue3(ssa.OpSliceMake, n.Type, ptr, len, len)
case OCFUNC:
- aux := s.lookupSymbol(n, &ssa.ExternSymbol{Sym: n.Left.Sym.Linksym()})
+ aux := n.Left.Sym.Linksym()
return s.entryNewValue1A(ssa.OpAddr, n.Type, aux, s.sb)
case ONAME:
if n.Class() == PFUNC {
// "value" of a function is the address of the function's closure
sym := funcsym(n.Sym).Linksym()
- aux := s.lookupSymbol(n, &ssa.ExternSymbol{Sym: sym})
- return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type), aux, s.sb)
+ return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type), sym, s.sb)
}
if s.canSSA(n) {
return s.variable(n, n.Type)
r := s.rtcall(growslice, true, []*types.Type{pt, types.Types[TINT], types.Types[TINT]}, taddr, p, l, c, nl)
if inplace {
- if sn.Op == ONAME {
+ if sn.Op == ONAME && sn.Class() != PEXTERN {
// Tell liveness we're about to build a new slice
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, sn, s.mem())
}
}
// Left is not ssa-able. Compute its address.
addr := s.addr(left, false)
- if left.Op == ONAME && skip == 0 {
+ if left.Op == ONAME && left.Class() != PEXTERN && skip == 0 {
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, left, s.mem())
}
if isReflectHeaderDataField(left) {
sys.ARM64)
makeOnesCountAMD64 := func(op64 ssa.Op, op32 ssa.Op) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- aux := s.lookupSymbol(n, &ssa.ExternSymbol{Sym: syslook("support_popcnt").Sym.Linksym()})
+ aux := syslook("support_popcnt").Sym.Linksym()
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), aux, s.sb)
v := s.newValue2(ssa.OpLoad, types.Types[TBOOL], addr, s.mem())
b := s.endBlock()
return 0
}
-// lookupSymbol is used to retrieve the symbol (Extern, Arg or Auto) used for a particular node.
-// This improves the effectiveness of cse by using the same Aux values for the
-// same symbols.
-func (s *state) lookupSymbol(n *Node, sym interface{}) interface{} {
- switch sym.(type) {
- default:
- s.Fatalf("sym %v is of unknown type %T", sym, sym)
- case *ssa.ExternSymbol, *ssa.ArgSymbol, *ssa.AutoSymbol:
- // these are the only valid types
- }
-
- if lsym, ok := s.varsyms[n]; ok {
- return lsym
- }
- s.varsyms[n] = sym
- return sym
-}
-
// addr converts the address of the expression n to SSA, adds it to s and returns the SSA result.
// The value that the returned Value represents is guaranteed to be non-nil.
// If bounded is true then this address does not require a nil check for its operand
switch n.Class() {
case PEXTERN:
// global variable
- aux := s.lookupSymbol(n, &ssa.ExternSymbol{Sym: n.Sym.Linksym()})
- v := s.entryNewValue1A(ssa.OpAddr, t, aux, s.sb)
+ v := s.entryNewValue1A(ssa.OpAddr, t, n.Sym.Linksym(), s.sb)
// TODO: Make OpAddr use AuxInt as well as Aux.
if n.Xoffset != 0 {
v = s.entryNewValue1I(ssa.OpOffPtr, v.Type, n.Xoffset, v)
}
if n == nodfp {
// Special arg that points to the frame pointer (Used by ORECOVER).
- aux := s.lookupSymbol(n, &ssa.ArgSymbol{Node: n})
- return s.entryNewValue1A(ssa.OpAddr, t, aux, s.sp)
+ return s.entryNewValue1A(ssa.OpAddr, t, n, s.sp)
}
s.Fatalf("addr of undeclared ONAME %v. declared: %v", n, s.decladdrs)
return nil
case PAUTO:
- aux := s.lookupSymbol(n, &ssa.AutoSymbol{Node: n})
- return s.newValue1A(ssa.OpAddr, t, aux, s.sp)
+ return s.newValue1A(ssa.OpAddr, t, n, s.sp)
case PPARAMOUT: // Same as PAUTO -- cannot generate LEA early.
// ensure that we reuse symbols for out parameters so
// that cse works on their addresses
- aux := s.lookupSymbol(n, &ssa.ArgSymbol{Node: n})
- return s.newValue1A(ssa.OpAddr, t, aux, s.sp)
+ return s.newValue1A(ssa.OpAddr, t, n, s.sp)
default:
s.Fatalf("variable address class %v not implemented", classnames[n.Class()])
return nil
if v.Aux == nil {
return 0
}
- switch sym := v.Aux.(type) {
-
- case *ssa.AutoSymbol:
- n := sym.Node.(*Node)
+ n, ok := v.Aux.(*Node)
+ if !ok {
+ v.Fatalf("bad aux type in %s\n", v.LongString())
+ }
+ if n.Class() == PAUTO {
return n.Xoffset
}
return 0
return
}
// Add symbol's offset from its base register.
- switch sym := v.Aux.(type) {
- case *ssa.ExternSymbol:
+ switch n := v.Aux.(type) {
+ case *obj.LSym:
a.Name = obj.NAME_EXTERN
- a.Sym = sym.Sym
- case *ssa.ArgSymbol:
- n := sym.Node.(*Node)
- a.Name = obj.NAME_PARAM
- a.Sym = n.Orig.Sym.Linksym()
- a.Offset += n.Xoffset
- case *ssa.AutoSymbol:
- n := sym.Node.(*Node)
+ a.Sym = n
+ case *Node:
+ if n.Class() == PPARAM || n.Class() == PPARAMOUT {
+ a.Name = obj.NAME_PARAM
+ a.Sym = n.Orig.Sym.Linksym()
+ a.Offset += n.Xoffset
+ break
+ }
a.Name = obj.NAME_AUTO
a.Sym = n.Sym.Linksym()
a.Offset += n.Xoffset
e.strings = make(map[string]interface{})
}
data := stringsym(s)
- aux := &ssa.ExternSymbol{Sym: data}
- e.strings[s] = aux
- return aux
+ e.strings[s] = data
+ return data
}
func (e *ssafn) Auto(pos src.XPos, t *types.Type) ssa.GCNode {
func (n *Node) Typ() *types.Type {
return n.Type
}
+func (n *Node) StorageClass() ssa.StorageClass {
+ switch n.Class() {
+ case PPARAM:
+ return ssa.ClassParam
+ case PPARAMOUT:
+ return ssa.ClassParamOut
+ case PAUTO:
+ return ssa.ClassAuto
+ default:
+ Fatalf("untranslateable storage class for %v: %s", n, n.Class())
+ return 0
+ }
+}
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
- case *ssa.ExternSymbol:
+ case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
- case *ssa.ArgSymbol, *ssa.AutoSymbol:
+ case *gc.Node:
wantreg = "SP"
gc.AddAux(&p.From, v)
case nil:
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
- case *ssa.ExternSymbol:
+ case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
- case *ssa.ArgSymbol, *ssa.AutoSymbol:
+ case *gc.Node:
wantreg = "SP"
gc.AddAux(&p.From, v)
case nil:
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
- case *ssa.ExternSymbol:
+ case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
- case *ssa.ArgSymbol, *ssa.AutoSymbol:
+ case *gc.Node:
wantreg = "SP"
gc.AddAux(&p.From, v)
case nil:
UseWriteBarrier() bool
}
-// interface used to hold *gc.Node. We'd use *gc.Node directly but
-// that would lead to an import cycle.
+// interface used to hold a *gc.Node (a stack variable).
+// We'd use *gc.Node directly but that would lead to an import cycle.
type GCNode interface {
Typ() *types.Type
String() string
+ StorageClass() StorageClass
}
+type StorageClass uint8
+
+const (
+ ClassAuto StorageClass = iota // local stack variable
+ ClassParam // argument
+ ClassParamOut // return value
+)
+
// NewConfig returns a new configuration object for the given architecture.
func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config {
c := &Config{arch: arch, Types: types}
}
}
-// elimUnreadAutos deletes stores to autos that are never read from.
+// elimUnreadAutos deletes stores (and associated bookkeeping ops VarDef and VarKill)
+// to autos that are never read from.
func elimUnreadAutos(f *Func) {
// Loop over all ops that affect autos taking note of which
// autos we need and also stores that we might be able to
var stores []*Value
for _, b := range f.Blocks {
for _, v := range b.Values {
- var sym *AutoSymbol
- sym, ok := v.Aux.(*AutoSymbol)
+ n, ok := v.Aux.(GCNode)
if !ok {
continue
}
+ if n.StorageClass() != ClassAuto {
+ continue
+ }
effect := v.Op.SymEffect()
switch effect {
- case SymWrite:
+ case SymNone, SymWrite:
// If we haven't seen the auto yet
// then this might be a store we can
// eliminate.
- if !seen[sym.Node] {
+ if !seen[n] {
stores = append(stores, v)
}
default:
// because dead loads haven't been
// eliminated yet.
if v.Uses > 0 {
- seen[sym.Node] = true
+ seen[n] = true
}
}
}
// Eliminate stores to unread autos.
for _, store := range stores {
- sym, _ := store.Aux.(*AutoSymbol)
- if seen[sym.Node] {
+ n, _ := store.Aux.(GCNode)
+ if seen[n] {
continue
}
return d.s
}
+func (d *DummyAuto) StorageClass() StorageClass {
+ return ClassAuto
+}
+
func (DummyFrontend) StringData(s string) interface{} {
return nil
}
// Constant-like things
{name: "InitMem"}, // memory input to the function.
- {name: "Arg", aux: "SymOff", symEffect: "None"}, // argument to the function. aux=GCNode of arg, off = offset in that arg.
+ {name: "Arg", aux: "SymOff", symEffect: "Read"}, // argument to the function. aux=GCNode of arg, off = offset in that arg.
- // The address of a variable. arg0 is the base pointer (SB or SP, depending
- // on whether it is a global or stack variable). The Aux field identifies the
- // variable. It will be either an *ExternSymbol (with arg0=SB), *ArgSymbol (arg0=SP),
- // or *AutoSymbol (arg0=SP).
+ // The address of a variable. arg0 is the base pointer.
+ // If the variable is a global, the base pointer will be SB and
+ // the Aux field will be a *obj.LSym.
+ // If the variable is a local, the base pointer will be SP and
+ // the Aux field will be a *gc.Node.
{name: "Addr", argLength: 1, aux: "Sym", symEffect: "Addr"}, // Address of a variable. Arg0=SP or SB. Aux identifies the variable.
{name: "SP"}, // stack pointer
{name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem", symEffect: "None"}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem
{name: "VarKill", argLength: 1, aux: "Sym", symEffect: "None"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
- {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "None"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
+ {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
{name: "KeepAlive", argLength: 2, typ: "Mem"}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem
{name: "RegKill"}, // regalloc has determined that the value in this register is dead
auxFloat32 // auxInt is a float32 (encoded with math.Float64bits)
auxFloat64 // auxInt is a float64 (encoded with math.Float64bits)
auxString // aux is a string
- auxSym // aux is a symbol
+ auxSym // aux is a symbol (a *gc.Node for locals or an *obj.LSym for globals)
auxSymOff // aux is a symbol, auxInt is an offset
auxSymValAndOff // aux is a symbol, auxInt is a ValAndOff
auxTyp // aux is a type
name: "Arg",
auxType: auxSymOff,
argLen: 0,
- symEffect: SymNone,
+ symEffect: SymRead,
generic: true,
},
{
name: "VarLive",
auxType: auxSym,
argLen: 1,
- symEffect: SymNone,
+ symEffect: SymRead,
generic: true,
},
{
return true
}
-// isArg returns whether s is an arg symbol
-func isArg(s interface{}) bool {
- _, ok := s.(*ArgSymbol)
- return ok
-}
-
-// isAuto returns whether s is an auto symbol
-func isAuto(s interface{}) bool {
- _, ok := s.(*AutoSymbol)
- return ok
-}
-
// isSameSym returns whether sym is the same as the given named symbol
func isSameSym(sym interface{}, name string) bool {
s, ok := sym.(fmt.Stringer)
// 'sym' is the symbol for the itab
func devirt(v *Value, sym interface{}, offset int64) *obj.LSym {
f := v.Block.Func
- ext, ok := sym.(*ExternSymbol)
+ n, ok := sym.(*obj.LSym)
if !ok {
return nil
}
- lsym := f.fe.DerefItab(ext.Sym, offset)
+ lsym := f.fe.DerefItab(n, offset)
if f.pass.debug > 0 {
if lsym != nil {
f.Warnl(v.Pos, "de-virtualizing call")
import (
"cmd/compile/internal/types"
- "cmd/internal/obj"
"cmd/internal/src"
"fmt"
"math"
return v != nil && (v.Op == OpConst64 || v.Op == OpConst32 || v.Op == OpConst16 || v.Op == OpConst8)
}
-// ExternSymbol is an aux value that encodes a variable's
-// constant offset from the static base pointer.
-type ExternSymbol struct {
- Sym *obj.LSym
- // Note: the offset for an external symbol is not
- // calculated until link time.
-}
-
-// ArgSymbol is an aux value that encodes an argument or result
-// variable's constant offset from FP (FP = SP + framesize).
-type ArgSymbol struct {
- Node GCNode // A *gc.Node referring to the argument/result variable.
-}
-
-// AutoSymbol is an aux value that encodes a local variable's
-// constant offset from SP.
-type AutoSymbol struct {
- Node GCNode // A *gc.Node referring to a local (auto) variable.
-}
-
-func (s *ExternSymbol) String() string {
- return s.Sym.String()
-}
-
-func (s *ArgSymbol) String() string {
- return s.Node.String()
-}
-
-func (s *AutoSymbol) String() string {
- return s.Node.String()
-}
-
// Reg returns the register assigned to v, in cmd/internal/obj/$ARCH numbering.
func (v *Value) Reg() int16 {
reg := v.Block.Func.RegAlloc[v.ID]
if sp == nil {
sp = f.Entry.NewValue0(initpos, OpSP, f.Config.Types.Uintptr)
}
- wbsym := &ExternSymbol{Sym: f.fe.Syslook("writeBarrier")}
+ wbsym := f.fe.Syslook("writeBarrier")
wbaddr = f.Entry.NewValue1A(initpos, OpAddr, f.Config.Types.UInt32Ptr, wbsym, sb)
writebarrierptr = f.fe.Syslook("writebarrierptr")
typedmemmove = f.fe.Syslook("typedmemmove")
pos := w.Pos
var fn *obj.LSym
- var typ *ExternSymbol
+ var typ *obj.LSym
var val *Value
switch w.Op {
case OpStoreWB:
case OpMoveWB:
fn = typedmemmove
val = w.Args[1]
- typ = &ExternSymbol{Sym: w.Aux.(*types.Type).Symbol()}
+ typ = w.Aux.(*types.Type).Symbol()
case OpZeroWB:
fn = typedmemclr
- typ = &ExternSymbol{Sym: w.Aux.(*types.Type).Symbol()}
+ typ = w.Aux.(*types.Type).Symbol()
case OpVarDef, OpVarLive, OpVarKill:
}
// wbcall emits write barrier runtime call in b, returns memory.
// if valIsVolatile, it moves val into temp space before making the call.
-func wbcall(pos src.XPos, b *Block, fn *obj.LSym, typ *ExternSymbol, ptr, val, mem, sp, sb *Value, valIsVolatile bool) *Value {
+func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Value, valIsVolatile bool) *Value {
config := b.Func.Config
var tmp GCNode
// value we're trying to move.
t := val.Type.ElemType()
tmp = b.Func.fe.Auto(val.Pos, t)
- aux := &AutoSymbol{Node: tmp}
mem = b.NewValue1A(pos, OpVarDef, types.TypeMem, tmp, mem)
- tmpaddr := b.NewValue1A(pos, OpAddr, t.PtrTo(), aux, sp)
+ tmpaddr := b.NewValue1A(pos, OpAddr, t.PtrTo(), tmp, sp)
siz := t.Size()
mem = b.NewValue3I(pos, OpMove, types.TypeMem, siz, tmpaddr, val, mem)
mem.Aux = t