Commit 02f4d0a1 authored by Keith Randall's avatar Keith Randall

[dev.ssa] cmd/compile: start arguments as spilled

Declare a function's arguments as having already been
spilled so their use just requires a restore.

Allow spill locations to be portions of larger objects the stack.
Required to load portions of compound input arguments.

Rename the memory input to InputMem.  Use Arg for the
pre-spilled argument values.

Change-Id: I8fe2a03ffbba1022d98bfae2052b376b96d32dda
Reviewed-on: https://go-review.googlesource.com/16536
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarDavid Chase <drchase@google.com>
parent 582baae2
...@@ -484,6 +484,9 @@ func compile(fn *Node) { ...@@ -484,6 +484,9 @@ func compile(fn *Node) {
if ssafn != nil && usessa { if ssafn != nil && usessa {
genssa(ssafn, ptxt, gcargs, gclocals) genssa(ssafn, ptxt, gcargs, gclocals)
if Curfn.Func.Endlineno != 0 {
lineno = Curfn.Func.Endlineno
}
return return
} }
Genlist(Curfn.Func.Enter) Genlist(Curfn.Func.Enter)
......
...@@ -97,7 +97,7 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) { ...@@ -97,7 +97,7 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
// Allocate starting values // Allocate starting values
s.labels = map[string]*ssaLabel{} s.labels = map[string]*ssaLabel{}
s.labeledNodes = map[*Node]*ssaLabel{} s.labeledNodes = map[*Node]*ssaLabel{}
s.startmem = s.entryNewValue0(ssa.OpArg, ssa.TypeMem) s.startmem = s.entryNewValue0(ssa.OpInitMem, ssa.TypeMem)
s.sp = s.entryNewValue0(ssa.OpSP, Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead s.sp = s.entryNewValue0(ssa.OpSP, Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
s.sb = s.entryNewValue0(ssa.OpSB, Types[TUINTPTR]) s.sb = s.entryNewValue0(ssa.OpSB, Types[TUINTPTR])
...@@ -3168,6 +3168,12 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name *Node) *ssa.Val ...@@ -3168,6 +3168,12 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name *Node) *ssa.Val
if name == &memVar { if name == &memVar {
return s.startmem return s.startmem
} }
if canSSA(name) {
v := s.entryNewValue0A(ssa.OpArg, t, name)
// v starts with AuxInt == 0.
s.addNamedValue(name, v)
return v
}
// variable is live at the entry block. Load it. // variable is live at the entry block. Load it.
addr := s.decladdrs[name] addr := s.decladdrs[name]
if addr == nil { if addr == nil {
...@@ -3239,18 +3245,21 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) { ...@@ -3239,18 +3245,21 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) {
// Don't track autotmp_ variables. // Don't track autotmp_ variables.
return return
} }
if n.Class == PPARAM || n.Class == PPARAMOUT { if n.Class == PAUTO && (v.Type.IsString() || v.Type.IsSlice() || v.Type.IsInterface()) {
// TODO: Remove this // TODO: can't handle auto compound objects with pointers yet.
// The live variable analysis barfs because we don't put VARDEF
// pseudos in the right place when we spill to these nodes.
return return
} }
if n.Class == PAUTO && n.Xoffset != 0 { if n.Class == PAUTO && n.Xoffset != 0 {
s.Fatalf("AUTO var with offset %s %d", n, n.Xoffset) s.Fatalf("AUTO var with offset %s %d", n, n.Xoffset)
} }
values, ok := s.f.NamedValues[n] loc := ssa.LocalSlot{N: n, Type: n.Type, Off: 0}
values, ok := s.f.NamedValues[loc]
if !ok { if !ok {
s.f.Names = append(s.f.Names, n) s.f.Names = append(s.f.Names, loc)
} }
s.f.NamedValues[n] = append(values, v) s.f.NamedValues[loc] = append(values, v)
} }
// an unresolved branch // an unresolved branch
...@@ -3873,11 +3882,17 @@ func (s *genState) genValue(v *ssa.Value) { ...@@ -3873,11 +3882,17 @@ func (s *genState) genValue(v *ssa.Value) {
return return
} }
p := Prog(movSizeByType(v.Type)) p := Prog(movSizeByType(v.Type))
n := autoVar(v.Args[0]) n, off := autoVar(v.Args[0])
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_AUTO
p.From.Node = n p.From.Node = n
p.From.Sym = Linksym(n.Sym) p.From.Sym = Linksym(n.Sym)
p.From.Offset = off
if n.Class == PPARAM {
p.From.Name = obj.NAME_PARAM
p.From.Offset += n.Xoffset
} else {
p.From.Name = obj.NAME_AUTO
}
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v) p.To.Reg = regnum(v)
...@@ -3889,11 +3904,17 @@ func (s *genState) genValue(v *ssa.Value) { ...@@ -3889,11 +3904,17 @@ func (s *genState) genValue(v *ssa.Value) {
p := Prog(movSizeByType(v.Type)) p := Prog(movSizeByType(v.Type))
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
n := autoVar(v) n, off := autoVar(v)
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_AUTO
p.To.Node = n p.To.Node = n
p.To.Sym = Linksym(n.Sym) p.To.Sym = Linksym(n.Sym)
p.To.Offset = off
if n.Class == PPARAM {
p.To.Name = obj.NAME_PARAM
p.To.Offset += n.Xoffset
} else {
p.To.Name = obj.NAME_AUTO
}
case ssa.OpPhi: case ssa.OpPhi:
// just check to make sure regalloc and stackalloc did it right // just check to make sure regalloc and stackalloc did it right
if v.Type.IsMemory() { if v.Type.IsMemory() {
...@@ -3912,9 +3933,10 @@ func (s *genState) genValue(v *ssa.Value) { ...@@ -3912,9 +3933,10 @@ func (s *genState) genValue(v *ssa.Value) {
v.Fatalf("const value %v shouldn't have a location", v) v.Fatalf("const value %v shouldn't have a location", v)
} }
case ssa.OpArg: case ssa.OpInitMem:
// memory arg needs no code // memory arg needs no code
// TODO: check that only mem arg goes here. case ssa.OpArg:
// input args need no code
case ssa.OpAMD64LoweredGetClosurePtr: case ssa.OpAMD64LoweredGetClosurePtr:
// Output is hardwired to DX only, // Output is hardwired to DX only,
// and DX contains the closure pointer on // and DX contains the closure pointer on
...@@ -4476,9 +4498,11 @@ func regnum(v *ssa.Value) int16 { ...@@ -4476,9 +4498,11 @@ func regnum(v *ssa.Value) int16 {
return ssaRegToReg[reg.(*ssa.Register).Num] return ssaRegToReg[reg.(*ssa.Register).Num]
} }
// autoVar returns a *Node representing the auto variable assigned to v. // autoVar returns a *Node and int64 representing the auto variable and offset within it
func autoVar(v *ssa.Value) *Node { // where v should be spilled.
return v.Block.Func.RegAlloc[v.ID].(*ssa.LocalSlot).N.(*Node) func autoVar(v *ssa.Value) (*Node, int64) {
loc := v.Block.Func.RegAlloc[v.ID].(ssa.LocalSlot)
return loc.N.(*Node), loc.Off
} }
// ssaExport exports a bunch of compiler services for the ssa backend. // ssaExport exports a bunch of compiler services for the ssa backend.
......
...@@ -83,8 +83,8 @@ type pass struct { ...@@ -83,8 +83,8 @@ type pass struct {
var passes = [...]pass{ var passes = [...]pass{
{"phielim", phielim}, {"phielim", phielim},
{"copyelim", copyelim}, {"copyelim", copyelim},
{"decompose", decompose},
{"early deadcode", deadcode}, // remove generated dead code to avoid doing pointless work during opt {"early deadcode", deadcode}, // remove generated dead code to avoid doing pointless work during opt
{"decompose", decompose},
{"opt", opt}, {"opt", opt},
{"opt deadcode", deadcode}, // remove any blocks orphaned during opt {"opt deadcode", deadcode}, // remove any blocks orphaned during opt
{"generic cse", cse}, {"generic cse", cse},
......
...@@ -103,7 +103,7 @@ func (c *Config) Frontend() Frontend { return c.fe } ...@@ -103,7 +103,7 @@ func (c *Config) Frontend() Frontend { return c.fe }
// NewFunc returns a new, empty function object // NewFunc returns a new, empty function object
func (c *Config) NewFunc() *Func { func (c *Config) NewFunc() *Func {
// TODO(khr): should this function take name, type, etc. as arguments? // TODO(khr): should this function take name, type, etc. as arguments?
return &Func{Config: c, NamedValues: map[GCNode][]*Value{}} return &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
} }
func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) } func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) }
......
...@@ -162,24 +162,38 @@ func deadcode(f *Func) { ...@@ -162,24 +162,38 @@ func deadcode(f *Func) {
} }
f.Blocks = f.Blocks[:i] f.Blocks = f.Blocks[:i]
// Remove dead entries from namedValues map. // Remove dead & duplicate entries from namedValues map.
for name, values := range f.NamedValues { s := newSparseSet(f.NumValues())
i := 0 i = 0
for _, name := range f.Names {
j := 0
s.clear()
values := f.NamedValues[name]
for _, v := range values { for _, v := range values {
for v.Op == OpCopy { for v.Op == OpCopy {
v = v.Args[0] v = v.Args[0]
} }
if live[v.ID] { if live[v.ID] && !s.contains(v.ID) {
values[i] = v values[j] = v
j++
s.add(v.ID)
}
}
if j == 0 {
delete(f.NamedValues, name)
} else {
f.Names[i] = name
i++ i++
for k := len(values) - 1; k >= j; k-- {
values[k] = nil
} }
f.NamedValues[name] = values[:j]
} }
f.NamedValues[name] = values[:i]
tail := values[i:]
for j := range tail {
tail[j] = nil
} }
for k := len(f.Names) - 1; k >= i; k-- {
f.Names[k] = LocalSlot{}
} }
f.Names = f.Names[:i]
// TODO: renumber Blocks and Values densely? // TODO: renumber Blocks and Values densely?
// TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it? // TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it?
......
...@@ -10,7 +10,7 @@ func TestDeadLoop(t *testing.T) { ...@@ -10,7 +10,7 @@ func TestDeadLoop(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem")), Exit("mem")),
...@@ -40,7 +40,7 @@ func TestDeadValue(t *testing.T) { ...@@ -40,7 +40,7 @@ func TestDeadValue(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("deadval", OpConst64, TypeInt64, 37, nil), Valu("deadval", OpConst64, TypeInt64, 37, nil),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
...@@ -64,7 +64,7 @@ func TestNeverTaken(t *testing.T) { ...@@ -64,7 +64,7 @@ func TestNeverTaken(t *testing.T) {
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("cond", OpConstBool, TypeBool, 0, nil), Valu("cond", OpConstBool, TypeBool, 0, nil),
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
If("cond", "then", "else")), If("cond", "then", "else")),
Bloc("then", Bloc("then",
Goto("exit")), Goto("exit")),
...@@ -98,7 +98,7 @@ func TestNestedDeadBlocks(t *testing.T) { ...@@ -98,7 +98,7 @@ func TestNestedDeadBlocks(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("cond", OpConstBool, TypeBool, 0, nil), Valu("cond", OpConstBool, TypeBool, 0, nil),
If("cond", "b2", "b4")), If("cond", "b2", "b4")),
Bloc("b2", Bloc("b2",
......
...@@ -12,7 +12,7 @@ func TestDeadStore(t *testing.T) { ...@@ -12,7 +12,7 @@ func TestDeadStore(t *testing.T) {
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("start", OpArg, TypeMem, 0, ".mem"), Valu("start", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Valu("v", OpConstBool, TypeBool, 1, nil), Valu("v", OpConstBool, TypeBool, 1, nil),
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"), Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
...@@ -47,7 +47,7 @@ func TestDeadStorePhi(t *testing.T) { ...@@ -47,7 +47,7 @@ func TestDeadStorePhi(t *testing.T) {
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("start", OpArg, TypeMem, 0, ".mem"), Valu("start", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Valu("v", OpConstBool, TypeBool, 1, nil), Valu("v", OpConstBool, TypeBool, 1, nil),
Valu("addr", OpAddr, ptrType, 0, nil, "sb"), Valu("addr", OpAddr, ptrType, 0, nil, "sb"),
...@@ -74,7 +74,7 @@ func TestDeadStoreTypes(t *testing.T) { ...@@ -74,7 +74,7 @@ func TestDeadStoreTypes(t *testing.T) {
t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"} t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"}
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("start", OpArg, TypeMem, 0, ".mem"), Valu("start", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Valu("v", OpConstBool, TypeBool, 1, nil), Valu("v", OpConstBool, TypeBool, 1, nil),
Valu("addr1", OpAddr, t1, 0, nil, "sb"), Valu("addr1", OpAddr, t1, 0, nil, "sb"),
......
...@@ -29,8 +29,75 @@ func decompose(f *Func) { ...@@ -29,8 +29,75 @@ func decompose(f *Func) {
} }
} }
} }
// TODO: decompose complex?
// TODO: decompose 64-bit ops on 32-bit archs? // TODO: decompose 64-bit ops on 32-bit archs?
// Split up named values into their components.
// NOTE: the component values we are making are dead at this point.
// We must do the opt pass before any deadcode elimination or we will
// lose the name->value correspondence.
for _, name := range f.Names {
t := name.Type
switch {
case t.IsComplex():
var elemType Type
if t.Size() == 16 {
elemType = f.Config.fe.TypeFloat64()
} else {
elemType = f.Config.fe.TypeFloat32()
}
rName := LocalSlot{name.N, elemType, name.Off}
iName := LocalSlot{name.N, elemType, name.Off + elemType.Size()}
f.Names = append(f.Names, rName, iName)
for _, v := range f.NamedValues[name] {
r := v.Block.NewValue1(v.Line, OpComplexReal, elemType, v)
i := v.Block.NewValue1(v.Line, OpComplexImag, elemType, v)
f.NamedValues[rName] = append(f.NamedValues[rName], r)
f.NamedValues[iName] = append(f.NamedValues[iName], i)
}
case t.IsString():
ptrType := f.Config.fe.TypeBytePtr()
lenType := f.Config.fe.TypeInt()
ptrName := LocalSlot{name.N, ptrType, name.Off}
lenName := LocalSlot{name.N, lenType, name.Off + f.Config.PtrSize}
f.Names = append(f.Names, ptrName, lenName)
for _, v := range f.NamedValues[name] {
ptr := v.Block.NewValue1(v.Line, OpStringPtr, ptrType, v)
len := v.Block.NewValue1(v.Line, OpStringLen, lenType, v)
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], ptr)
f.NamedValues[lenName] = append(f.NamedValues[lenName], len)
}
case t.IsSlice():
ptrType := f.Config.fe.TypeBytePtr()
lenType := f.Config.fe.TypeInt()
ptrName := LocalSlot{name.N, ptrType, name.Off}
lenName := LocalSlot{name.N, lenType, name.Off + f.Config.PtrSize}
capName := LocalSlot{name.N, lenType, name.Off + 2*f.Config.PtrSize}
f.Names = append(f.Names, ptrName, lenName, capName)
for _, v := range f.NamedValues[name] {
ptr := v.Block.NewValue1(v.Line, OpSlicePtr, ptrType, v)
len := v.Block.NewValue1(v.Line, OpSliceLen, lenType, v)
cap := v.Block.NewValue1(v.Line, OpSliceCap, lenType, v)
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], ptr)
f.NamedValues[lenName] = append(f.NamedValues[lenName], len)
f.NamedValues[capName] = append(f.NamedValues[capName], cap)
}
case t.IsInterface():
ptrType := f.Config.fe.TypeBytePtr()
typeName := LocalSlot{name.N, ptrType, name.Off}
dataName := LocalSlot{name.N, ptrType, name.Off + f.Config.PtrSize}
f.Names = append(f.Names, typeName, dataName)
for _, v := range f.NamedValues[name] {
typ := v.Block.NewValue1(v.Line, OpITab, ptrType, v)
data := v.Block.NewValue1(v.Line, OpIData, ptrType, v)
f.NamedValues[typeName] = append(f.NamedValues[typeName], typ)
f.NamedValues[dataName] = append(f.NamedValues[dataName], data)
}
//case t.IsStruct():
// TODO
case t.Size() > f.Config.IntSize:
f.Unimplementedf("undecomposed type %s", t)
}
}
} }
func decomposeStringPhi(v *Value) { func decomposeStringPhi(v *Value) {
......
...@@ -20,7 +20,7 @@ func genLinear(size int) []bloc { ...@@ -20,7 +20,7 @@ func genLinear(size int) []bloc {
var blocs []bloc var blocs []bloc
blocs = append(blocs, blocs = append(blocs,
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto(blockn(0)), Goto(blockn(0)),
), ),
) )
...@@ -43,7 +43,7 @@ func genFwdBack(size int) []bloc { ...@@ -43,7 +43,7 @@ func genFwdBack(size int) []bloc {
var blocs []bloc var blocs []bloc
blocs = append(blocs, blocs = append(blocs,
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
Goto(blockn(0)), Goto(blockn(0)),
), ),
...@@ -73,7 +73,7 @@ func genManyPred(size int) []bloc { ...@@ -73,7 +73,7 @@ func genManyPred(size int) []bloc {
var blocs []bloc var blocs []bloc
blocs = append(blocs, blocs = append(blocs,
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
Goto(blockn(0)), Goto(blockn(0)),
), ),
...@@ -111,7 +111,7 @@ func genMaxPred(size int) []bloc { ...@@ -111,7 +111,7 @@ func genMaxPred(size int) []bloc {
var blocs []bloc var blocs []bloc
blocs = append(blocs, blocs = append(blocs,
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
Goto(blockn(0)), Goto(blockn(0)),
), ),
...@@ -136,7 +136,7 @@ func genMaxPredValue(size int) []bloc { ...@@ -136,7 +136,7 @@ func genMaxPredValue(size int) []bloc {
var blocs []bloc var blocs []bloc
blocs = append(blocs, blocs = append(blocs,
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
Goto(blockn(0)), Goto(blockn(0)),
), ),
...@@ -223,7 +223,7 @@ func TestDominatorsSingleBlock(t *testing.T) { ...@@ -223,7 +223,7 @@ func TestDominatorsSingleBlock(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Exit("mem"))) Exit("mem")))
doms := map[string]string{} doms := map[string]string{}
...@@ -238,7 +238,7 @@ func TestDominatorsSimple(t *testing.T) { ...@@ -238,7 +238,7 @@ func TestDominatorsSimple(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("a")), Goto("a")),
Bloc("a", Bloc("a",
Goto("b")), Goto("b")),
...@@ -266,7 +266,7 @@ func TestDominatorsMultPredFwd(t *testing.T) { ...@@ -266,7 +266,7 @@ func TestDominatorsMultPredFwd(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
If("p", "a", "c")), If("p", "a", "c")),
Bloc("a", Bloc("a",
...@@ -294,7 +294,7 @@ func TestDominatorsDeadCode(t *testing.T) { ...@@ -294,7 +294,7 @@ func TestDominatorsDeadCode(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 0, nil), Valu("p", OpConstBool, TypeBool, 0, nil),
If("p", "b3", "b5")), If("p", "b3", "b5")),
Bloc("b2", Exit("mem")), Bloc("b2", Exit("mem")),
...@@ -319,7 +319,7 @@ func TestDominatorsMultPredRev(t *testing.T) { ...@@ -319,7 +319,7 @@ func TestDominatorsMultPredRev(t *testing.T) {
Bloc("entry", Bloc("entry",
Goto("first")), Goto("first")),
Bloc("first", Bloc("first",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
Goto("a")), Goto("a")),
Bloc("a", Bloc("a",
...@@ -348,7 +348,7 @@ func TestDominatorsMultPred(t *testing.T) { ...@@ -348,7 +348,7 @@ func TestDominatorsMultPred(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
If("p", "a", "c")), If("p", "a", "c")),
Bloc("a", Bloc("a",
...@@ -376,7 +376,7 @@ func TestPostDominators(t *testing.T) { ...@@ -376,7 +376,7 @@ func TestPostDominators(t *testing.T) {
c := testConfig(t) c := testConfig(t)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
If("p", "a", "c")), If("p", "a", "c")),
Bloc("a", Bloc("a",
...@@ -403,7 +403,7 @@ func TestInfiniteLoop(t *testing.T) { ...@@ -403,7 +403,7 @@ func TestInfiniteLoop(t *testing.T) {
// note lack of an exit block // note lack of an exit block
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("p", OpConstBool, TypeBool, 1, nil), Valu("p", OpConstBool, TypeBool, 1, nil),
Goto("a")), Goto("a")),
Bloc("a", Bloc("a",
......
...@@ -26,12 +26,11 @@ type Func struct { ...@@ -26,12 +26,11 @@ type Func struct {
// when register allocation is done, maps value ids to locations // when register allocation is done, maps value ids to locations
RegAlloc []Location RegAlloc []Location
// map from *gc.Node to set of Values that represent that Node. // map from LocalSlot to set of Values that we want to store in that slot.
// The Node must be an ONAME with PPARAM, PPARAMOUT, or PAUTO class. NamedValues map[LocalSlot][]*Value
NamedValues map[GCNode][]*Value
// Names is a copy of NamedValues.Keys. We keep a separate list // Names is a copy of NamedValues.Keys. We keep a separate list
// of keys to make iteration order deterministic. // of keys to make iteration order deterministic.
Names []GCNode Names []LocalSlot
} }
// NumBlocks returns an integer larger than the id of any Block in the Func. // NumBlocks returns an integer larger than the id of any Block in the Func.
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
// //
// fun := Fun("entry", // fun := Fun("entry",
// Bloc("entry", // Bloc("entry",
// Valu("mem", OpArg, TypeMem, 0, ".mem"), // Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
// Goto("exit")), // Goto("exit")),
// Bloc("exit", // Bloc("exit",
// Exit("mem")), // Exit("mem")),
...@@ -263,7 +263,7 @@ func TestArgs(t *testing.T) { ...@@ -263,7 +263,7 @@ func TestArgs(t *testing.T) {
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))) Exit("mem")))
...@@ -286,7 +286,7 @@ func TestEquiv(t *testing.T) { ...@@ -286,7 +286,7 @@ func TestEquiv(t *testing.T) {
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
...@@ -295,7 +295,7 @@ func TestEquiv(t *testing.T) { ...@@ -295,7 +295,7 @@ func TestEquiv(t *testing.T) {
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
...@@ -307,7 +307,7 @@ func TestEquiv(t *testing.T) { ...@@ -307,7 +307,7 @@ func TestEquiv(t *testing.T) {
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
...@@ -318,7 +318,7 @@ func TestEquiv(t *testing.T) { ...@@ -318,7 +318,7 @@ func TestEquiv(t *testing.T) {
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit"))), Goto("exit"))),
}, },
} }
...@@ -335,26 +335,26 @@ func TestEquiv(t *testing.T) { ...@@ -335,26 +335,26 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Exit("mem"))), Exit("mem"))),
}, },
// value order changed // value order changed
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Exit("mem"))), Exit("mem"))),
...@@ -363,12 +363,12 @@ func TestEquiv(t *testing.T) { ...@@ -363,12 +363,12 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 26, nil), Valu("a", OpConst64, TypeInt64, 26, nil),
Exit("mem"))), Exit("mem"))),
}, },
...@@ -376,12 +376,12 @@ func TestEquiv(t *testing.T) { ...@@ -376,12 +376,12 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 0, 14), Valu("a", OpConst64, TypeInt64, 0, 14),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 0, 26), Valu("a", OpConst64, TypeInt64, 0, 26),
Exit("mem"))), Exit("mem"))),
}, },
...@@ -389,14 +389,14 @@ func TestEquiv(t *testing.T) { ...@@ -389,14 +389,14 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 14, nil), Valu("a", OpConst64, TypeInt64, 14, nil),
Valu("b", OpConst64, TypeInt64, 26, nil), Valu("b", OpConst64, TypeInt64, 26, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("a", OpConst64, TypeInt64, 0, nil), Valu("a", OpConst64, TypeInt64, 0, nil),
Valu("b", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 14, nil),
Valu("sum", OpAdd64, TypeInt64, 0, nil, "b", "a"), Valu("sum", OpAdd64, TypeInt64, 0, nil, "b", "a"),
......
...@@ -188,12 +188,12 @@ ...@@ -188,12 +188,12 @@
(Load <t> ptr mem) && t.IsString() -> (Load <t> ptr mem) && t.IsString() ->
(StringMake (StringMake
(Load <config.fe.TypeBytePtr()> ptr mem) (Load <config.fe.TypeBytePtr()> ptr mem)
(Load <config.fe.TypeUintptr()> (Load <config.fe.TypeInt()>
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] ptr) (OffPtr <config.fe.TypeInt().PtrTo()> [config.PtrSize] ptr)
mem)) mem))
(Store [2*config.PtrSize] dst (StringMake ptr len) mem) -> (Store [2*config.PtrSize] dst (StringMake ptr len) mem) ->
(Store [config.PtrSize] (Store [config.PtrSize]
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] dst) (OffPtr <config.fe.TypeInt().PtrTo()> [config.PtrSize] dst)
len len
(Store [config.PtrSize] dst ptr mem)) (Store [config.PtrSize] dst ptr mem))
...@@ -215,18 +215,18 @@ ...@@ -215,18 +215,18 @@
(Load <t> ptr mem) && t.IsSlice() -> (Load <t> ptr mem) && t.IsSlice() ->
(SliceMake (SliceMake
(Load <config.fe.TypeBytePtr()> ptr mem) (Load <config.fe.TypeBytePtr()> ptr mem)
(Load <config.fe.TypeUintptr()> (Load <config.fe.TypeInt()>
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] ptr) (OffPtr <config.fe.TypeInt().PtrTo()> [config.PtrSize] ptr)
mem) mem)
(Load <config.fe.TypeUintptr()> (Load <config.fe.TypeInt()>
(OffPtr <config.fe.TypeUintptr().PtrTo()> [2*config.PtrSize] ptr) (OffPtr <config.fe.TypeInt().PtrTo()> [2*config.PtrSize] ptr)
mem)) mem))
(Store [3*config.PtrSize] dst (SliceMake ptr len cap) mem) -> (Store [3*config.PtrSize] dst (SliceMake ptr len cap) mem) ->
(Store [config.PtrSize] (Store [config.PtrSize]
(OffPtr <config.fe.TypeUintptr().PtrTo()> [2*config.PtrSize] dst) (OffPtr <config.fe.TypeInt().PtrTo()> [2*config.PtrSize] dst)
cap cap
(Store [config.PtrSize] (Store [config.PtrSize]
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] dst) (OffPtr <config.fe.TypeInt().PtrTo()> [config.PtrSize] dst)
len len
(Store [config.PtrSize] dst ptr mem))) (Store [config.PtrSize] dst ptr mem)))
...@@ -261,3 +261,30 @@ ...@@ -261,3 +261,30 @@
// Get rid of Convert ops for pointer arithmetic on unsafe.Pointer. // Get rid of Convert ops for pointer arithmetic on unsafe.Pointer.
(Convert (Add64 (Convert ptr) off)) -> (Add64 ptr off) (Convert (Add64 (Convert ptr) off)) -> (Add64 ptr off)
// Decompose compound argument values
(Arg {n} [off]) && v.Type.IsString() ->
(StringMake
(Arg <config.fe.TypeBytePtr()> {n} [off])
(Arg <config.fe.TypeInt()> {n} [off+config.PtrSize]))
(Arg {n} [off]) && v.Type.IsSlice() ->
(SliceMake
(Arg <config.fe.TypeBytePtr()> {n} [off])
(Arg <config.fe.TypeInt()> {n} [off+config.PtrSize])
(Arg <config.fe.TypeInt()> {n} [off+2*config.PtrSize]))
(Arg {n} [off]) && v.Type.IsInterface() ->
(IMake
(Arg <config.fe.TypeBytePtr()> {n} [off])
(Arg <config.fe.TypeBytePtr()> {n} [off+config.PtrSize]))
(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 16 ->
(ComplexMake
(Arg <config.fe.TypeFloat64()> {n} [off])
(Arg <config.fe.TypeFloat64()> {n} [off+8]))
(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 8 ->
(ComplexMake
(Arg <config.fe.TypeFloat32()> {n} [off])
(Arg <config.fe.TypeFloat32()> {n} [off+4]))
...@@ -260,7 +260,8 @@ var genericOps = []opData{ ...@@ -260,7 +260,8 @@ var genericOps = []opData{
// TODO: Const32F, ... // TODO: Const32F, ...
// Constant-like things // Constant-like things
{name: "Arg"}, // memory input to the function. {name: "InitMem"}, // memory input to the function.
{name: "Arg"}, // 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 // 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 // on whether it is a global or stack variable). The Aux field identifies the
......
...@@ -472,3 +472,7 @@ func (p htmlFuncPrinter) startDepCycle() { ...@@ -472,3 +472,7 @@ func (p htmlFuncPrinter) startDepCycle() {
func (p htmlFuncPrinter) endDepCycle() { func (p htmlFuncPrinter) endDepCycle() {
fmt.Fprintln(p.w, "</span>") fmt.Fprintln(p.w, "</span>")
} }
func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) {
// TODO
}
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package ssa package ssa
import "fmt"
// A place that an ssa variable can reside. // A place that an ssa variable can reside.
type Location interface { type Location interface {
Name() string // name to use in assembly templates: %rax, 16(%rsp), ... Name() string // name to use in assembly templates: %rax, 16(%rsp), ...
...@@ -21,10 +23,16 @@ func (r *Register) Name() string { ...@@ -21,10 +23,16 @@ func (r *Register) Name() string {
} }
// A LocalSlot is a location in the stack frame. // A LocalSlot is a location in the stack frame.
// It is (possibly a subpiece of) a PPARAM, PPARAMOUT, or PAUTO ONAME node.
type LocalSlot struct { type LocalSlot struct {
N GCNode // a *gc.Node for an auto variable N GCNode // an ONAME *gc.Node representing a variable on the stack
Type Type // type of slot
Off int64 // offset of slot in N
} }
func (s *LocalSlot) Name() string { func (s LocalSlot) Name() string {
return s.N.String() if s.Off == 0 {
return fmt.Sprintf("%s[%s]", s.N, s.Type)
}
return fmt.Sprintf("%s+%d[%s]", s.N, s.Off, s.Type)
} }
...@@ -21,7 +21,7 @@ func checkLower(f *Func) { ...@@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered continue // lowered
} }
switch v.Op { switch v.Op {
case OpSP, OpSB, OpArg, OpCopy, OpPhi, OpVarDef, OpVarKill: case OpSP, OpSB, OpInitMem, OpArg, OpCopy, OpPhi, OpVarDef, OpVarKill:
continue // ok not to lower continue // ok not to lower
} }
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString() s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()
......
...@@ -21,7 +21,7 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) { ...@@ -21,7 +21,7 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) {
var blocs []bloc var blocs []bloc
blocs = append(blocs, blocs = append(blocs,
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto(blockn(0)), Goto(blockn(0)),
), ),
...@@ -67,7 +67,7 @@ func TestNilcheckSimple(t *testing.T) { ...@@ -67,7 +67,7 @@ func TestNilcheckSimple(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -104,7 +104,7 @@ func TestNilcheckDomOrder(t *testing.T) { ...@@ -104,7 +104,7 @@ func TestNilcheckDomOrder(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -140,7 +140,7 @@ func TestNilcheckAddr(t *testing.T) { ...@@ -140,7 +140,7 @@ func TestNilcheckAddr(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -173,7 +173,7 @@ func TestNilcheckAddPtr(t *testing.T) { ...@@ -173,7 +173,7 @@ func TestNilcheckAddPtr(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -207,7 +207,7 @@ func TestNilcheckPhi(t *testing.T) { ...@@ -207,7 +207,7 @@ func TestNilcheckPhi(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Valu("sp", OpSP, TypeInvalid, 0, nil), Valu("sp", OpSP, TypeInvalid, 0, nil),
Valu("baddr", OpAddr, TypeBool, 0, "b", "sp"), Valu("baddr", OpAddr, TypeBool, 0, "b", "sp"),
...@@ -251,7 +251,7 @@ func TestNilcheckKeepRemove(t *testing.T) { ...@@ -251,7 +251,7 @@ func TestNilcheckKeepRemove(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -299,7 +299,7 @@ func TestNilcheckInFalseBranch(t *testing.T) { ...@@ -299,7 +299,7 @@ func TestNilcheckInFalseBranch(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -350,7 +350,7 @@ func TestNilcheckUser(t *testing.T) { ...@@ -350,7 +350,7 @@ func TestNilcheckUser(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
...@@ -389,7 +389,7 @@ func TestNilcheckBug(t *testing.T) { ...@@ -389,7 +389,7 @@ func TestNilcheckBug(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t}, nil) c := NewConfig("amd64", DummyFrontend{t}, nil)
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sb", OpSB, TypeInvalid, 0, nil),
Goto("checkPtr")), Goto("checkPtr")),
Bloc("checkPtr", Bloc("checkPtr",
......
...@@ -475,6 +475,7 @@ const ( ...@@ -475,6 +475,7 @@ const (
OpConst64F OpConst64F
OpConstInterface OpConstInterface
OpConstSlice OpConstSlice
OpInitMem
OpArg OpArg
OpAddr OpAddr
OpSP OpSP
...@@ -3987,6 +3988,10 @@ var opcodeTable = [...]opInfo{ ...@@ -3987,6 +3988,10 @@ var opcodeTable = [...]opInfo{
name: "ConstSlice", name: "ConstSlice",
generic: true, generic: true,
}, },
{
name: "InitMem",
generic: true,
},
{ {
name: "Arg", name: "Arg",
generic: true, generic: true,
......
...@@ -28,6 +28,7 @@ type funcPrinter interface { ...@@ -28,6 +28,7 @@ type funcPrinter interface {
value(v *Value, live bool) value(v *Value, live bool)
startDepCycle() startDepCycle()
endDepCycle() endDepCycle()
named(n LocalSlot, vals []*Value)
} }
type stringFuncPrinter struct { type stringFuncPrinter struct {
...@@ -73,6 +74,10 @@ func (p stringFuncPrinter) startDepCycle() { ...@@ -73,6 +74,10 @@ func (p stringFuncPrinter) startDepCycle() {
func (p stringFuncPrinter) endDepCycle() {} func (p stringFuncPrinter) endDepCycle() {}
func (p stringFuncPrinter) named(n LocalSlot, vals []*Value) {
fmt.Fprintf(p.w, "name %s: %v\n", n.Name(), vals)
}
func fprintFunc(p funcPrinter, f *Func) { func fprintFunc(p funcPrinter, f *Func) {
reachable, live := findlive(f) reachable, live := findlive(f)
p.header(f) p.header(f)
...@@ -136,4 +141,7 @@ func fprintFunc(p funcPrinter, f *Func) { ...@@ -136,4 +141,7 @@ func fprintFunc(p funcPrinter, f *Func) {
p.endBlock(b) p.endBlock(b)
} }
for name, vals := range f.NamedValues {
p.named(name, vals)
}
} }
...@@ -759,6 +759,16 @@ func (s *regAllocState) regalloc(f *Func) { ...@@ -759,6 +759,16 @@ func (s *regAllocState) regalloc(f *Func) {
pc++ pc++
continue continue
} }
if v.Op == OpArg {
// Args are "pre-spilled" values. We don't allocate
// any register here. We just set up the spill pointer to
// point at itself and any later user will restore it to use it.
s.values[v.ID].spill = v
s.values[v.ID].spillUsed = true // use is guaranteed
b.Values = append(b.Values, v)
pc++
continue
}
s.clearUses(pc*2 - 1) s.clearUses(pc*2 - 1)
regspec := opcodeTable[v.Op].reg regspec := opcodeTable[v.Op].reg
if regDebug { if regDebug {
......
...@@ -10,7 +10,7 @@ func TestLiveControlOps(t *testing.T) { ...@@ -10,7 +10,7 @@ func TestLiveControlOps(t *testing.T) {
c := testConfig(t) c := testConfig(t)
f := Fun(c, "entry", f := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("x", OpAMD64MOVBconst, TypeInt8, 0, 1), Valu("x", OpAMD64MOVBconst, TypeInt8, 0, 1),
Valu("y", OpAMD64MOVBconst, TypeInt8, 0, 2), Valu("y", OpAMD64MOVBconst, TypeInt8, 0, 2),
Valu("a", OpAMD64TESTB, TypeBool, 0, nil, "x", "y"), Valu("a", OpAMD64TESTB, TypeBool, 0, nil, "x", "y"),
......
...@@ -11,7 +11,7 @@ func TestSchedule(t *testing.T) { ...@@ -11,7 +11,7 @@ func TestSchedule(t *testing.T) {
cases := []fun{ cases := []fun{
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem0", OpArg, TypeMem, 0, ".mem"), Valu("mem0", OpInitMem, TypeMem, 0, ".mem"),
Valu("ptr", OpConst64, TypeInt64, 0xABCD, nil), Valu("ptr", OpConst64, TypeInt64, 0xABCD, nil),
Valu("v", OpConst64, TypeInt64, 12, nil), Valu("v", OpConst64, TypeInt64, 12, nil),
Valu("mem1", OpStore, TypeMem, 8, nil, "ptr", "v", "mem0"), Valu("mem1", OpStore, TypeMem, 8, nil, "ptr", "v", "mem0"),
......
...@@ -28,7 +28,7 @@ func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun { ...@@ -28,7 +28,7 @@ func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun {
ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"} ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"}
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("mem", OpInitMem, TypeMem, 0, ".mem"),
Valu("SP", OpSP, TypeUInt64, 0, nil), Valu("SP", OpSP, TypeUInt64, 0, nil),
Valu("argptr", OpOffPtr, ptyp, 8, nil, "SP"), Valu("argptr", OpOffPtr, ptyp, 8, nil, "SP"),
Valu("resptr", OpOffPtr, ptyp, 16, nil, "SP"), Valu("resptr", OpOffPtr, ptyp, 16, nil, "SP"),
......
...@@ -44,6 +44,13 @@ func stackalloc(f *Func) { ...@@ -44,6 +44,13 @@ func stackalloc(f *Func) {
} }
case v.Op == OpLoadReg: case v.Op == OpLoadReg:
s.add(v.Args[0].ID) s.add(v.Args[0].ID)
case v.Op == OpArg:
// This is an input argument which is pre-spilled. It is kind of
// like a StoreReg, but we don't remove v.ID here because we want
// this value to appear live even before this point. Being live
// all the way to the start of the entry block prevents other
// values from being allocated to the same slot and clobbering
// the input value before we have a chance to load it.
} }
} }
} }
...@@ -51,7 +58,7 @@ func stackalloc(f *Func) { ...@@ -51,7 +58,7 @@ func stackalloc(f *Func) {
// Build map from values to their names, if any. // Build map from values to their names, if any.
// A value may be associated with more than one name (e.g. after // A value may be associated with more than one name (e.g. after
// the assignment i=j). This step picks one name per value arbitrarily. // the assignment i=j). This step picks one name per value arbitrarily.
names := make([]GCNode, f.NumValues()) names := make([]LocalSlot, f.NumValues())
for _, name := range f.Names { for _, name := range f.Names {
// Note: not "range f.NamedValues" above, because // Note: not "range f.NamedValues" above, because
// that would be nondeterministic. // that would be nondeterministic.
...@@ -74,9 +81,17 @@ func stackalloc(f *Func) { ...@@ -74,9 +81,17 @@ func stackalloc(f *Func) {
} }
} }
// Allocate args to their assigned locations.
for _, v := range f.Entry.Values {
if v.Op != OpArg {
continue
}
f.setHome(v, LocalSlot{v.Aux.(GCNode), v.Type, v.AuxInt})
}
// For each type, we keep track of all the stack slots we // For each type, we keep track of all the stack slots we
// have allocated for that type. // have allocated for that type.
locations := map[Type][]*LocalSlot{} locations := map[Type][]LocalSlot{}
// Each time we assign a stack slot to a value v, we remember // Each time we assign a stack slot to a value v, we remember
// the slot we used via an index into locations[v.Type]. // the slot we used via an index into locations[v.Type].
...@@ -99,16 +114,16 @@ func stackalloc(f *Func) { ...@@ -99,16 +114,16 @@ func stackalloc(f *Func) {
// If this is a named value, try to use the name as // If this is a named value, try to use the name as
// the spill location. // the spill location.
var name GCNode var name LocalSlot
if v.Op == OpStoreReg { if v.Op == OpStoreReg {
name = names[v.Args[0].ID] name = names[v.Args[0].ID]
} else { } else {
name = names[v.ID] name = names[v.ID]
} }
if name != nil && v.Type.Equal(name.Typ()) { if name.N != nil && v.Type.Equal(name.Type) {
for _, id := range interfere[v.ID] { for _, id := range interfere[v.ID] {
h := f.getHome(id) h := f.getHome(id)
if h != nil && h.(*LocalSlot).N == name { if h != nil && h.(LocalSlot) == name {
// A variable can interfere with itself. // A variable can interfere with itself.
// It is rare, but but it can happen. // It is rare, but but it can happen.
goto noname goto noname
...@@ -118,17 +133,16 @@ func stackalloc(f *Func) { ...@@ -118,17 +133,16 @@ func stackalloc(f *Func) {
for _, a := range v.Args { for _, a := range v.Args {
for _, id := range interfere[a.ID] { for _, id := range interfere[a.ID] {
h := f.getHome(id) h := f.getHome(id)
if h != nil && h.(*LocalSlot).N == name { if h != nil && h.(LocalSlot) == name {
goto noname goto noname
} }
} }
} }
} }
loc := &LocalSlot{name} f.setHome(v, name)
f.setHome(v, loc)
if v.Op == OpPhi { if v.Op == OpPhi {
for _, a := range v.Args { for _, a := range v.Args {
f.setHome(a, loc) f.setHome(a, name)
} }
} }
continue continue
...@@ -169,7 +183,7 @@ func stackalloc(f *Func) { ...@@ -169,7 +183,7 @@ func stackalloc(f *Func) {
} }
// If there is no unused stack slot, allocate a new one. // If there is no unused stack slot, allocate a new one.
if i == len(locs) { if i == len(locs) {
locs = append(locs, &LocalSlot{f.Config.fe.Auto(v.Type)}) locs = append(locs, LocalSlot{N: f.Config.fe.Auto(v.Type), Type: v.Type, Off: 0})
locations[v.Type] = locs locations[v.Type] = locs
} }
// Use the stack variable at that index for v. // Use the stack variable at that index for v.
......
...@@ -54,8 +54,8 @@ func tighten(f *Func) { ...@@ -54,8 +54,8 @@ func tighten(f *Func) {
for _, b := range f.Blocks { for _, b := range f.Blocks {
for i := 0; i < len(b.Values); i++ { for i := 0; i < len(b.Values); i++ {
v := b.Values[i] v := b.Values[i]
if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert { if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert || v.Op == OpArg {
// GetClosurePtr must stay in entry block. // GetClosurePtr & Arg must stay in entry block.
// OpConvert must not float over call sites. // OpConvert must not float over call sites.
// TODO do we instead need a dependence edge of some sort for OpConvert? // TODO do we instead need a dependence edge of some sort for OpConvert?
// Would memory do the trick, or do we need something else that relates // Would memory do the trick, or do we need something else that relates
......
...@@ -94,9 +94,6 @@ func TestGdbPython(t *testing.T) { ...@@ -94,9 +94,6 @@ func TestGdbPython(t *testing.T) {
"-ex", "echo END\n", "-ex", "echo END\n",
"-ex", "echo BEGIN print strvar\n", "-ex", "echo BEGIN print strvar\n",
"-ex", "print strvar", "-ex", "print strvar",
"-ex", "echo END\n",
"-ex", "echo BEGIN print ptrvar\n",
"-ex", "print ptrvar",
"-ex", "echo END\n"} "-ex", "echo END\n"}
// without framepointer, gdb cannot backtrace our non-standard // without framepointer, gdb cannot backtrace our non-standard
...@@ -151,10 +148,6 @@ func TestGdbPython(t *testing.T) { ...@@ -151,10 +148,6 @@ func TestGdbPython(t *testing.T) {
t.Fatalf("print strvar failed: %s", bl) t.Fatalf("print strvar failed: %s", bl)
} }
if bl := blocks["print ptrvar"]; !strVarRe.MatchString(bl) {
t.Fatalf("print ptrvar failed: %s", bl)
}
btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`) btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`)
if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) { if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) {
t.Fatalf("goroutine 2 bt failed: %s", bl) t.Fatalf("goroutine 2 bt failed: %s", bl)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment