Commit 9f954db1 authored by Keith Randall's avatar Keith Randall

[dev.ssa] cmd/compile: add decompose pass

Decompose breaks compound objects up into pieces that can be
operated on by the target architecture.  The decompose pass only
does phi ops, the rest is done by the rewrite rules in generic.rules.

Compound objects include strings,slices,interfaces,structs,arrays.

Arrays aren't decomposed because of indexing (we could support
constant indexes, but dynamic indexes can't be handled using SSA).
Structs will come in a subsequent CL.

TODO: after this pass we have lost the association between, e.g.,
a string's pointer and its size.  It would be nice if we could keep
that information around for debugging info somehow.

Change-Id: I6379ab962a7beef62297d0f68c421f22aa0a0901
Reviewed-on: https://go-review.googlesource.com/13683Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
parent 8d23681c
......@@ -1072,7 +1072,15 @@ func (s *state) expr(n *Node) *ssa.Value {
case CTBOOL:
return s.entryNewValue0A(ssa.OpConstBool, n.Type, n.Val().U)
case CTNIL:
return s.entryNewValue0(ssa.OpConstNil, n.Type)
t := n.Type
switch {
case t.IsSlice():
return s.entryNewValue0(ssa.OpConstSlice, t)
case t.IsInterface():
return s.entryNewValue0(ssa.OpConstInterface, t)
default:
return s.entryNewValue0(ssa.OpConstNil, t)
}
case CTFLT:
f := n.Val().U.(*Mpflt)
switch n.Type.Size() {
......@@ -1470,6 +1478,10 @@ func (s *state) zeroVal(t *Type) *ssa.Value {
return s.entryNewValue0(ssa.OpConstNil, t)
case t.IsBoolean():
return s.entryNewValue0A(ssa.OpConstBool, t, false) // TODO: store bools as 0/1 in AuxInt?
case t.IsInterface():
return s.entryNewValue0(ssa.OpConstInterface, t)
case t.IsSlice():
return s.entryNewValue0(ssa.OpConstSlice, t)
}
s.Unimplementedf("zero for type %v not implemented", t)
return nil
......@@ -1582,11 +1594,47 @@ func canSSA(n *Node) bool {
if n.Class == PPARAMOUT {
return false
}
if Isfat(n.Type) {
return canSSAType(n.Type)
// TODO: try to make more variables SSAable?
}
// canSSA reports whether variables of type t are SSA-able.
func canSSAType(t *Type) bool {
dowidth(t)
if t.Width > int64(4*Widthptr) {
// 4*Widthptr is an arbitrary constant. We want it
// to be at least 3*Widthptr so slices can be registerized.
// Too big and we'll introduce too much register pressure.
return false
}
return true
// TODO: try to make more variables SSAable.
switch t.Etype {
case TARRAY:
if Isslice(t) {
return true
}
// We can't do arrays because dynamic indexing is
// not supported on SSA variables.
// TODO: maybe allow if length is <=1? All indexes
// are constant? Might be good for the arrays
// introduced by the compiler for variadic functions.
return false
case TSTRUCT:
if countfield(t) > 4 {
// 4 is an arbitrary constant. Same reasoning
// as above, lots of small fields would waste
// register space needed by other values.
return false
}
for t1 := t.Type; t1 != nil; t1 = t1.Down {
if !canSSAType(t1.Type) {
return false
}
}
return false // until it is implemented
//return true
default:
return true
}
}
// nilCheck generates nil pointer checking code.
......
// run
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test compound objects
package main
import "fmt"
func string_ssa(a, b string, x bool) string {
s := ""
if x {
s = a
} else {
s = b
}
return s
}
func testString() {
a := "foo"
b := "barz"
if want, got := a, string_ssa(a, b, true); got != want {
fmt.Printf("string_ssa(%v, %v, true) = %v, want %v\n", a, b, got, want)
failed = true
}
if want, got := b, string_ssa(a, b, false); got != want {
fmt.Printf("string_ssa(%v, %v, false) = %v, want %v\n", a, b, got, want)
failed = true
}
}
func slice_ssa(a, b []byte, x bool) []byte {
var s []byte
if x {
s = a
} else {
s = b
}
return s
}
func testSlice() {
a := []byte{3, 4, 5}
b := []byte{7, 8, 9}
if want, got := byte(3), slice_ssa(a, b, true)[0]; got != want {
fmt.Printf("slice_ssa(%v, %v, true) = %v, want %v\n", a, b, got, want)
failed = true
}
if want, got := byte(7), slice_ssa(a, b, false)[0]; got != want {
fmt.Printf("slice_ssa(%v, %v, false) = %v, want %v\n", a, b, got, want)
failed = true
}
}
func interface_ssa(a, b interface{}, x bool) interface{} {
var s interface{}
if x {
s = a
} else {
s = b
}
return s
}
func testInterface() {
a := interface{}(3)
b := interface{}(4)
if want, got := 3, interface_ssa(a, b, true).(int); got != want {
fmt.Printf("interface_ssa(%v, %v, true) = %v, want %v\n", a, b, got, want)
failed = true
}
if want, got := 4, interface_ssa(a, b, false).(int); got != want {
fmt.Printf("interface_ssa(%v, %v, false) = %v, want %v\n", a, b, got, want)
failed = true
}
}
var failed = false
func main() {
testString()
testSlice()
testInterface()
if failed {
panic("failed")
}
}
......@@ -68,6 +68,10 @@ func (t *Type) IsSlice() bool {
return t.Etype == TARRAY && t.Bound < 0
}
func (t *Type) IsInterface() bool {
return t.Etype == TINTER
}
func (t *Type) Elem() ssa.Type {
return t.Type
}
......
......@@ -60,6 +60,7 @@ type pass struct {
var passes = [...]pass{
{"phielim", phielim},
{"copyelim", copyelim},
{"decompose", decompose},
{"early deadcode", deadcode}, // remove generated dead code to avoid doing pointless work during opt
{"opt", opt},
{"opt deadcode", deadcode}, // remove any blocks orphaned during opt
......@@ -103,6 +104,8 @@ var passOrder = [...]constraint{
// tighten will be most effective when as many values have been removed as possible
{"generic deadcode", "tighten"},
{"generic cse", "tighten"},
// don't run optimization pass until we've decomposed compound objects
{"decompose", "opt"},
// don't layout blocks until critical edges have been removed
{"critical", "layout"},
// regalloc requires the removal of all critical edges
......
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// decompose converts phi ops on compound types into phi
// ops on simple types.
// (The remaining compound ops are decomposed with rewrite rules.)
func decompose(f *Func) {
for _, b := range f.Blocks {
for _, v := range b.Values {
if v.Op != OpPhi {
continue
}
switch {
case v.Type.IsString():
decomposeStringPhi(v)
case v.Type.IsSlice():
decomposeSlicePhi(v)
case v.Type.IsInterface():
decomposeInterfacePhi(v)
//case v.Type.IsStruct():
// decomposeStructPhi(v)
case v.Type.Size() > f.Config.IntSize:
f.Unimplementedf("undecomposed type %s", v.Type)
}
}
}
// TODO: decompose complex?
// TODO: decompose 64-bit ops on 32-bit archs?
}
func decomposeStringPhi(v *Value) {
fe := v.Block.Func.Config.fe
ptrType := fe.TypeBytePtr()
lenType := fe.TypeUintptr()
ptr := v.Block.NewValue0(v.Line, OpPhi, ptrType)
len := v.Block.NewValue0(v.Line, OpPhi, lenType)
for _, a := range v.Args {
ptr.AddArg(a.Block.NewValue1(v.Line, OpStringPtr, ptrType, a))
len.AddArg(a.Block.NewValue1(v.Line, OpStringLen, lenType, a))
}
v.Op = OpStringMake
v.AuxInt = 0
v.Aux = nil
v.resetArgs()
v.AddArg(ptr)
v.AddArg(len)
}
func decomposeSlicePhi(v *Value) {
fe := v.Block.Func.Config.fe
ptrType := fe.TypeBytePtr()
lenType := fe.TypeUintptr()
ptr := v.Block.NewValue0(v.Line, OpPhi, ptrType)
len := v.Block.NewValue0(v.Line, OpPhi, lenType)
cap := v.Block.NewValue0(v.Line, OpPhi, lenType)
for _, a := range v.Args {
ptr.AddArg(a.Block.NewValue1(v.Line, OpSlicePtr, ptrType, a))
len.AddArg(a.Block.NewValue1(v.Line, OpSliceLen, lenType, a))
cap.AddArg(a.Block.NewValue1(v.Line, OpSliceCap, lenType, a))
}
v.Op = OpSliceMake
v.AuxInt = 0
v.Aux = nil
v.resetArgs()
v.AddArg(ptr)
v.AddArg(len)
v.AddArg(cap)
}
func decomposeInterfacePhi(v *Value) {
ptrType := v.Block.Func.Config.fe.TypeBytePtr()
itab := v.Block.NewValue0(v.Line, OpPhi, ptrType)
data := v.Block.NewValue0(v.Line, OpPhi, ptrType)
for _, a := range v.Args {
itab.AddArg(a.Block.NewValue1(v.Line, OpITab, ptrType, a))
data.AddArg(a.Block.NewValue1(v.Line, OpIData, ptrType, a))
}
v.Op = OpIMake
v.AuxInt = 0
v.Aux = nil
v.resetArgs()
v.AddArg(itab)
v.AddArg(data)
}
func decomposeStructPhi(v *Value) {
// TODO
}
......@@ -59,36 +59,90 @@
(Com32 (Com32 x)) -> x
(Com64 (Com64 x)) -> x
// tear apart slices
// TODO: anything that generates a slice needs to go in here.
(SlicePtr (Load ptr mem)) -> (Load ptr mem)
(SliceLen (Load ptr mem)) -> (Load (AddPtr <ptr.Type> ptr (ConstPtr <config.Frontend().TypeUintptr()> [config.PtrSize])) mem)
(SliceCap (Load ptr mem)) -> (Load (AddPtr <ptr.Type> ptr (ConstPtr <config.Frontend().TypeUintptr()> [config.PtrSize*2])) mem)
// slice and interface comparisons
// the frontend ensures that we can only compare against nil
// start by putting nil on the right to simplify the other rules
(EqFat x y) && x.Op == OpConstNil && y.Op != OpConstNil -> (EqFat y x)
(NeqFat x y) && x.Op == OpConstNil && y.Op != OpConstNil -> (NeqFat y x)
// it suffices to check the first word (backing array for slices, dynamic type for interfaces)
(EqFat (Load ptr mem) (ConstNil)) -> (EqPtr (Load <config.Frontend().TypeUintptr()> ptr mem) (ConstPtr <config.Frontend().TypeUintptr()> [0]))
(NeqFat (Load ptr mem) (ConstNil)) -> (NeqPtr (Load <config.Frontend().TypeUintptr()> ptr mem) (ConstPtr <config.Frontend().TypeUintptr()> [0]))
(EqFat (Load ptr mem) (ConstNil)) -> (EqPtr (Load <config.fe.TypeUintptr()> ptr mem) (ConstPtr <config.fe.TypeUintptr()> [0]))
(NeqFat (Load ptr mem) (ConstNil)) -> (NeqPtr (Load <config.fe.TypeUintptr()> ptr mem) (ConstPtr <config.fe.TypeUintptr()> [0]))
// indexing operations
// Note: bounds check has already been done
(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex <v.Type.PtrTo()> ptr idx) mem)
(PtrIndex <t> ptr idx) -> (AddPtr ptr (MulPtr <config.Frontend().TypeUintptr()> idx (ConstPtr <config.Frontend().TypeUintptr()> [t.Elem().Size()])))
(PtrIndex <t> ptr idx) -> (AddPtr ptr (MulPtr <config.fe.TypeUintptr()> idx (ConstPtr <config.fe.TypeUintptr()> [t.Elem().Size()])))
(StructSelect [idx] (Load ptr mem)) -> (Load (OffPtr <v.Type.PtrTo()> [idx] ptr) mem)
// big-object moves
(Store [size] dst (Load src mem) mem) && size > config.IntSize -> (Move [size] dst src mem)
// string ops
(ConstString {s}) -> (StringMake (Addr <config.Frontend().TypeBytePtr()> {config.fe.StringData(s.(string))} (SB <config.Frontend().TypeUintptr()>)) (ConstPtr <config.Frontend().TypeUintptr()> [int64(len(s.(string)))]))
(Load <t> ptr mem) && t.IsString() -> (StringMake (Load <config.Frontend().TypeBytePtr()> ptr mem) (Load <config.Frontend().TypeUintptr()> (OffPtr <config.Frontend().TypeBytePtr()> [config.PtrSize] ptr) mem))
(StringPtr (StringMake ptr _)) -> ptr
(StringLen (StringMake _ len)) -> len
(Store [2*config.PtrSize] dst str mem) && str.Type.IsString() -> (Store [config.PtrSize] (OffPtr <config.Frontend().TypeBytePtr()> [config.PtrSize] dst) (StringLen <config.Frontend().TypeUintptr()> str) (Store [config.PtrSize] <TypeMem> dst (StringPtr <config.Frontend().TypeBytePtr()> str) mem))
(ConstString {s}) ->
(StringMake
(Addr <config.fe.TypeBytePtr()> {config.fe.StringData(s.(string))}
(SB <config.fe.TypeUintptr()>))
(ConstPtr <config.fe.TypeUintptr()> [int64(len(s.(string)))]))
(Load <t> ptr mem) && t.IsString() ->
(StringMake
(Load <config.fe.TypeBytePtr()> ptr mem)
(Load <config.fe.TypeUintptr()>
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] ptr)
mem))
(Store [2*config.PtrSize] dst (StringMake ptr len) mem) ->
(Store [config.PtrSize]
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] dst)
len
(Store <TypeMem> [config.PtrSize] dst ptr mem))
// slice ops
(SlicePtr (SliceMake ptr _ _ )) -> ptr
(SliceLen (SliceMake _ len _)) -> len
(SliceCap (SliceMake _ _ cap)) -> cap
(ConstSlice) ->
(SliceMake
(ConstNil <config.fe.TypeBytePtr()>)
(ConstPtr <config.fe.TypeUintptr()>)
(ConstPtr <config.fe.TypeUintptr()>))
(Load <t> ptr mem) && t.IsSlice() ->
(SliceMake
(Load <config.fe.TypeBytePtr()> ptr mem)
(Load <config.fe.TypeUintptr()>
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] ptr)
mem)
(Load <config.fe.TypeUintptr()>
(OffPtr <config.fe.TypeUintptr().PtrTo()> [2*config.PtrSize] ptr)
mem))
(Store [3*config.PtrSize] dst (SliceMake ptr len cap) mem) ->
(Store [config.PtrSize]
(OffPtr <config.fe.TypeUintptr().PtrTo()> [2*config.PtrSize] dst)
cap
(Store <TypeMem> [config.PtrSize]
(OffPtr <config.fe.TypeUintptr().PtrTo()> [config.PtrSize] dst)
len
(Store <TypeMem> [config.PtrSize] dst ptr mem)))
// interface ops
(ITab (IMake itab _)) -> itab
(IData (IMake _ data)) -> data
(ConstInterface) ->
(IMake
(ConstNil <config.fe.TypeBytePtr()>)
(ConstNil <config.fe.TypeBytePtr()>))
(Load <t> ptr mem) && t.IsInterface() ->
(IMake
(Load <config.fe.TypeBytePtr()> ptr mem)
(Load <config.fe.TypeBytePtr()>
(OffPtr <config.fe.TypeBytePtr().PtrTo()> [config.PtrSize] ptr)
mem))
(Store [2*config.PtrSize] dst (IMake itab data) mem) ->
(Store [config.PtrSize]
(OffPtr <config.fe.TypeBytePtr().PtrTo()> [config.PtrSize] dst)
data
(Store <TypeMem> [config.PtrSize] dst itab mem))
// big-object moves (TODO: remove?)
(Store [size] dst (Load src mem) mem) && size > config.IntSize -> (Move [size] dst src mem)
(If (IsNonNil (GetG)) yes no) -> (Plain nil yes)
......
......@@ -229,7 +229,9 @@ var genericOps = []opData{
{name: "Const64"},
{name: "Const32F"},
{name: "Const64F"},
{name: "ConstPtr"}, // pointer-sized integer constant
{name: "ConstPtr"}, // pointer-sized integer constant
{name: "ConstInterface"}, // nil interface
{name: "ConstSlice"}, // nil slice
// TODO: Const32F, ...
// Constant-like things
......@@ -305,7 +307,9 @@ var genericOps = []opData{
{name: "StringLen"}, // len(arg0)
// Interfaces
{name: "ITab"}, // arg0=interface, returns itable field
{name: "IMake"}, // arg0=itab, arg1=data
{name: "ITab"}, // arg0=interface, returns itable field
{name: "IData"}, // arg0=interface, returns data field
// Spill&restore ops for the register allocator. These are
// semantically identical to OpCopy; they do not take/return
......
......@@ -396,6 +396,8 @@ const (
OpConst32F
OpConst64F
OpConstPtr
OpConstInterface
OpConstSlice
OpArg
OpAddr
OpSP
......@@ -442,7 +444,9 @@ const (
OpStringMake
OpStringPtr
OpStringLen
OpIMake
OpITab
OpIData
OpStoreReg
OpLoadReg
OpFwdRef
......@@ -3374,6 +3378,14 @@ var opcodeTable = [...]opInfo{
name: "ConstPtr",
generic: true,
},
{
name: "ConstInterface",
generic: true,
},
{
name: "ConstSlice",
generic: true,
},
{
name: "Arg",
generic: true,
......@@ -3558,10 +3570,18 @@ var opcodeTable = [...]opInfo{
name: "StringLen",
generic: true,
},
{
name: "IMake",
generic: true,
},
{
name: "ITab",
generic: true,
},
{
name: "IData",
generic: true,
},
{
name: "StoreReg",
generic: true,
......
......@@ -18,6 +18,8 @@ type Type interface {
IsFloat() bool
IsPtr() bool
IsString() bool
IsSlice() bool
IsInterface() bool
IsMemory() bool // special ssa-package-only types
IsFlags() bool
......@@ -36,19 +38,21 @@ type CompilerType struct {
Flags bool
}
func (t *CompilerType) Size() int64 { return 0 }
func (t *CompilerType) Alignment() int64 { return 0 }
func (t *CompilerType) IsBoolean() bool { return false }
func (t *CompilerType) IsInteger() bool { return false }
func (t *CompilerType) IsSigned() bool { return false }
func (t *CompilerType) IsFloat() bool { return false }
func (t *CompilerType) IsPtr() bool { return false }
func (t *CompilerType) IsString() bool { return false }
func (t *CompilerType) IsMemory() bool { return t.Memory }
func (t *CompilerType) IsFlags() bool { return t.Flags }
func (t *CompilerType) String() string { return t.Name }
func (t *CompilerType) Elem() Type { panic("not implemented") }
func (t *CompilerType) PtrTo() Type { panic("not implemented") }
func (t *CompilerType) Size() int64 { return 0 }
func (t *CompilerType) Alignment() int64 { return 0 }
func (t *CompilerType) IsBoolean() bool { return false }
func (t *CompilerType) IsInteger() bool { return false }
func (t *CompilerType) IsSigned() bool { return false }
func (t *CompilerType) IsFloat() bool { return false }
func (t *CompilerType) IsPtr() bool { return false }
func (t *CompilerType) IsString() bool { return false }
func (t *CompilerType) IsSlice() bool { return false }
func (t *CompilerType) IsInterface() bool { return false }
func (t *CompilerType) IsMemory() bool { return t.Memory }
func (t *CompilerType) IsFlags() bool { return t.Flags }
func (t *CompilerType) String() string { return t.Name }
func (t *CompilerType) Elem() Type { panic("not implemented") }
func (t *CompilerType) PtrTo() Type { panic("not implemented") }
func (t *CompilerType) Equal(u Type) bool {
x, ok := u.(*CompilerType)
......
......@@ -14,24 +14,28 @@ type TypeImpl struct {
Float bool
Ptr bool
string bool
slice bool
inter bool
Elem_ Type
Name string
}
func (t *TypeImpl) Size() int64 { return t.Size_ }
func (t *TypeImpl) Alignment() int64 { return t.Align }
func (t *TypeImpl) IsBoolean() bool { return t.Boolean }
func (t *TypeImpl) IsInteger() bool { return t.Integer }
func (t *TypeImpl) IsSigned() bool { return t.Signed }
func (t *TypeImpl) IsFloat() bool { return t.Float }
func (t *TypeImpl) IsPtr() bool { return t.Ptr }
func (t *TypeImpl) IsString() bool { return t.string }
func (t *TypeImpl) IsMemory() bool { return false }
func (t *TypeImpl) IsFlags() bool { return false }
func (t *TypeImpl) String() string { return t.Name }
func (t *TypeImpl) Elem() Type { return t.Elem_ }
func (t *TypeImpl) PtrTo() Type { panic("not implemented") }
func (t *TypeImpl) Size() int64 { return t.Size_ }
func (t *TypeImpl) Alignment() int64 { return t.Align }
func (t *TypeImpl) IsBoolean() bool { return t.Boolean }
func (t *TypeImpl) IsInteger() bool { return t.Integer }
func (t *TypeImpl) IsSigned() bool { return t.Signed }
func (t *TypeImpl) IsFloat() bool { return t.Float }
func (t *TypeImpl) IsPtr() bool { return t.Ptr }
func (t *TypeImpl) IsString() bool { return t.string }
func (t *TypeImpl) IsSlice() bool { return t.slice }
func (t *TypeImpl) IsInterface() bool { return t.inter }
func (t *TypeImpl) IsMemory() bool { return false }
func (t *TypeImpl) IsFlags() bool { return false }
func (t *TypeImpl) String() string { return t.Name }
func (t *TypeImpl) Elem() Type { return t.Elem_ }
func (t *TypeImpl) PtrTo() Type { panic("not implemented") }
func (t *TypeImpl) Equal(u Type) bool {
x, ok := u.(*TypeImpl)
......
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