diff --git a/docs/design/func.md b/docs/design/func.md index e21f4f7..706efc4 100644 --- a/docs/design/func.md +++ b/docs/design/func.md @@ -1,6 +1,8 @@ # pal functional architecture + + ## Memory Models Memory is modelled as a set of nodes (as in nodes in a graph), called _locs_. diff --git a/docs/index.md b/docs/index.md index 2104e18..d8cefc2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD # pal website Hello @@ -9,4 +8,3 @@ pal is a library for doing pointer analysis. ->>>>>>> proto diff --git a/indexing/constvals.go b/indexing/constvals.go index a429f01..a7b5c68 100644 --- a/indexing/constvals.go +++ b/indexing/constvals.go @@ -15,53 +15,109 @@ package indexing import ( + "fmt" "io" + "strconv" "github.com/go-air/pal/xtruth" ) type consts struct{} +type C struct{ p *int64 } + +func (c C) PlainEncode(w io.Writer) error { + if c.p == nil { + _, err := fmt.Fprintf(w, ".") + return err + } + _, err := w.Write(strconv.AppendInt(nil, *c.p, 16)) + return err +} + +func isHexLower(b byte) bool { + return (b >= byte('0') && b <= byte('9')) || (b >= byte('a') && b <= byte('f')) +} + +func (c C) PlainDecode(r io.Reader) error { + var buf [16]byte + _, err := io.ReadFull(r, buf[:1]) + if err != nil { + return err + } + if buf[0] == byte('.') { + c.p = nil + return nil + } + i := 1 + for i < 16 { + _, err = io.ReadFull(r, buf[i:i+1]) + if err != nil { + return err + } + if !isHexLower(buf[i]) { + break + } + + i++ + } + v, _ := strconv.ParseInt(string(buf[:i]), 16, 64) + *c.p = v + return nil +} func ConstVals() T { return consts{} } +var zero int64 = 0 +var one int64 = 1 + func (c consts) Zero() I { - return I(0) + z := int64(0) + return I(C{&z}) } func (c consts) One() I { - return I(1) + o := int64(1) + return I(C{&o}) } func (c consts) IsVar(v I) bool { - return false + return v.(C).p == nil } func (c consts) Var() I { - panic("constant variable requested.") + return C{nil} } -func (c consts) FromInt(v int) I { - return v +func (c consts) FromInt64(v int64) I { + return C{&v} } -func (c consts) ToInt(v I) (int, bool) { - vv, ok := v.(int) - if !ok { +func (c consts) ToInt64(v I) (int64, bool) { + p := v.(C).p + if p == nil { return 0, false } - return vv, true + return *p, true } func (c consts) Plus(a, b I) I { - aa, bb := a.(int), b.(int) - return I(aa + bb) + pa, pb := a.(C).p, b.(C).p + if pa == nil || pb == nil { + return c.Var() + } + r := *pa + *pb + return c.FromInt64(r) } func (c consts) Times(a, b I) I { - aa, bb := a.(int), b.(int) - return I(aa * bb) + pa, pb := a.(C).p, b.(C).p + if pa == nil || pb == nil { + return c.Var() + } + r := *pa * *pb + return c.FromInt64(r) } func (c consts) Div(a, b I) (I, xtruth.T) { @@ -69,9 +125,11 @@ func (c consts) Div(a, b I) (I, xtruth.T) { case xtruth.True: return c.Zero(), xtruth.False case xtruth.False: - return I(a.(int) / b.(int)), xtruth.True + pa, pb := a.(C).p, b.(C).p + r := *pa / *pb + return c.FromInt64(r), xtruth.True case xtruth.X: - panic("non-const op") + return c.Var(), xtruth.X } return c.Zero(), xtruth.X } @@ -81,21 +139,31 @@ func (c consts) Rem(a, b I) (I, xtruth.T) { case xtruth.True: return c.Zero(), xtruth.False case xtruth.False: - return I(a.(int) % b.(int)), xtruth.True + pa, pb := a.(C).p, b.(C).p + r := *pa % *pb + return c.FromInt64(r), xtruth.True case xtruth.X: - panic("non-const op") + return c.Var(), xtruth.X } return c.Zero(), xtruth.X } func (c consts) Band(a, b I) I { - aa, bb := a.(int), b.(int) - return I(aa & bb) + pa, pb := a.(C).p, b.(C).p + if pa == nil || pb == nil { + return c.Var() + } + z := *pa & *pb + return c.FromInt64(z) } func (c consts) Bnot(a I) I { - aa := a.(int) - return I(^aa) + pa := a.(C).p + if pa == nil { + return c.Var() + } + z := ^(*pa) + return c.FromInt64(z) } func (c consts) Lshift(a, s I) (I, xtruth.T) { @@ -107,16 +175,22 @@ func (c consts) Rshift(a, s I) (I, xtruth.T) { } func (c consts) Less(a, b I) xtruth.T { - aa, bb := a.(int), b.(int) - if aa < bb { + pa, pb := a.(C).p, b.(C).p + if pa == nil || pb == nil { + return xtruth.X + } + if *pa < *pb { return xtruth.True } return xtruth.False } func (c consts) Equal(a, b I) xtruth.T { - aa, bb := a.(int), b.(int) - if aa == bb { + pa, pb := a.(C).p, b.(C).p + if pa == nil || pb == nil { + return xtruth.X + } + if *pa == *pb { return xtruth.True } return xtruth.False diff --git a/indexing/t.go b/indexing/t.go index af36069..150aa6b 100644 --- a/indexing/t.go +++ b/indexing/t.go @@ -20,6 +20,7 @@ import ( ) type I interface { + plain.Coder } type T interface { @@ -27,9 +28,9 @@ type T interface { Zero() I One() I // replace with go/constant? - ToInt(v I) (i int, ok bool) + ToInt64(v I) (i int64, ok bool) - FromInt(i int) I + FromInt64(i int64) I IsVar(v I) bool Var() I diff --git a/internal/plain/plain.go b/internal/plain/plain.go index 656ebe2..92fce03 100644 --- a/internal/plain/plain.go +++ b/internal/plain/plain.go @@ -23,6 +23,7 @@ import ( "bytes" "fmt" "io" + "strconv" "strings" ) @@ -89,6 +90,39 @@ func DecodeJoin(r io.Reader, sep string, ds ...Decoder) error { return nil } +func isHexLower(b byte) bool { + return (b >= byte('0') && b <= byte('9')) || (b >= byte('a') && b <= byte('f')) +} + +func EncodeInt64(w io.Writer, v int64) error { + var buf [16]byte + _, err := w.Write(strconv.AppendInt(buf[:0], v, 16)) + return err +} + +func DecodeInt64(r io.Reader, p *int64) error { + var buf [16]byte + i := 0 + var err error + for i < 16 { + _, err = io.ReadFull(r, buf[i:i+1]) + if err != nil { + return err + } + if !isHexLower(buf[i]) { + break + } + + i++ + } + if i == 0 { + return fmt.Errorf("no plain i64 input") + } + v, _ := strconv.ParseInt(string(buf[:i]), 16, 64) + *p = v + return nil +} + type Coder interface { Encoder Decoder diff --git a/memory/constraint.go b/memory/constraint.go index e98ff01..8eaf481 100644 --- a/memory/constraint.go +++ b/memory/constraint.go @@ -82,10 +82,6 @@ func Store(dst, src Loc) Constraint { return Constraint{Kind: KStore, Dest: dst, Src: src} } -func Transfer(dst, src Loc) Constraint { - return TransferIndex(dst, src, 0) -} - func TransferIndex(dst, src Loc, i indexing.I) Constraint { return Constraint{Kind: KTransfer, Dest: dst, Src: src, Index: i} } diff --git a/memory/model.go b/memory/model.go index 2d5f11c..a4c0998 100644 --- a/memory/model.go +++ b/memory/model.go @@ -95,11 +95,7 @@ func (mod *Model) Field(m Loc, i int) Loc { n := m + 1 // first field for j := 0; j < i; j++ { sz := mod.locs[n].lsz - isz, ok := mod.indexing.ToInt(sz) - if !ok { - return NoLoc - } - n += Loc(isz) + n += Loc(sz) } return n } @@ -108,22 +104,17 @@ func (mod *Model) Field(m Loc, i int) Loc { func (mod *Model) ArrayIndex(m Loc, i int) Loc { n := m + 1 sz := mod.locs[n].lsz - isz, ok := mod.indexing.ToInt(sz) - if !ok { - return NoLoc - - } - n += Loc(i * isz) + n += Loc(i * sz) return n } -// LSize returns the virtual size of memory associated +// Lsize returns the virtual size of memory associated // with m. // // The virtual size is the size according to the model, // which is 1 + the sum of the the vsizes of all locations // n such that mod.Parent(n) == m. -func (mod *Model) LSize(m Loc) int { +func (mod *Model) Lsize(m Loc) int { return mod.locs[m].lsz } @@ -197,38 +188,6 @@ func (mod *Model) Cap(c int) { mod.locs = mod.locs[:c] } -// validates that -// -// n.vsz = 1 + Sum(c.vsz, c a child of n) -func (mod *Model) Check() error { - N := len(mod.locs) - sizes := make(map[Loc]int) - for i := 2; i < N; i++ { - loc := &mod.locs[i] - if loc.parent == Loc(i) { - continue - } - sz, ok := mod.indexing.ToInt(loc.lsz) - if !ok { - return fmt.Errorf("vsz not const") - } - ttl, _ := sizes[loc.parent] - sizes[loc.parent] = sz + ttl - } - for m, sz := range sizes { - sz++ - loc := &mod.locs[m] - realsz, ok := mod.indexing.ToInt(loc.lsz) - if !ok { - return fmt.Errorf("vsz not const") - } - if sz != realsz { - return fmt.Errorf("loc %s sum %d size %d\n", plain.String(m), sz, realsz) - } - } - return nil -} - // p_add adds a root recursively according to ty. // // add is responsible for setting the size, parent, class, attrs, typ, and root @@ -244,6 +203,7 @@ func (mod *Model) p_add(gp *GenParams, p, r Loc, sum *int) Loc { pos: gp.pos, typ: gp.typ} lastSum := *sum + added := true switch gp.ts.Kind(gp.typ) { // these are added as pointers here indirect associattions (params, // returns, ...) are done in github.com/go-air/objects.Builder @@ -269,26 +229,28 @@ func (mod *Model) p_add(gp *GenParams, p, r Loc, sum *int) Loc { nf := gp.ts.NumFields(gp.typ) styp := gp.typ for i := 0; i < nf; i++ { - _, gp.typ = gp.ts.Field(styp, i) + _, gp.typ, _ = gp.ts.Field(styp, i) mod.p_add(gp, n, r, sum) } case typeset.Tuple: - mod.locs = append(mod.locs, l) - *sum++ tn := gp.ts.ArrayLen(gp.typ) ttyp := gp.typ for i := 0; i < tn; i++ { - _, gp.typ = gp.ts.Field(ttyp, i) + _, gp.typ, _ = gp.ts.Field(ttyp, i) mod.p_add(gp, n, r, sum) } + added = false default: panic(fmt.Sprintf("%d: unexpected/unimplemented", gp.typ)) } - // we added a slot at dst[n] for ty, set its size - mod.locs[n].lsz = *sum - lastSum - return n + if added { + // we added a slot at dst[n] for ty, set its size + mod.locs[n].lsz = *sum - lastSum + return n + } + return NoLoc } // add adds a root recursively according to ty. @@ -402,7 +364,7 @@ func (mod *Model) AddStore(dst, src Loc) { // dst = src func (mod *Model) AddTransfer(dst, src Loc) { - mod.constraints = append(mod.constraints, Transfer(dst, src)) + mod.AddTransferIndex(dst, src, mod.indexing.Zero()) } func (mod *Model) AddTransferIndex(dst, src Loc, i indexing.I) { diff --git a/memory/model_test.go b/memory/model_test.go index 9556ae5..2063666 100644 --- a/memory/model_test.go +++ b/memory/model_test.go @@ -38,10 +38,6 @@ func TestModel(t *testing.T) { if mdl.Parent(gf) != gs { t.Errorf("field parent") } - if err := mdl.Check(); err != nil { - t.Errorf("check failed: %s", err) - return - } //fmt.Printf(plain.String(mdl)) } diff --git a/objects/array.go b/objects/array.go index b611eba..22f7051 100644 --- a/objects/array.go +++ b/objects/array.go @@ -17,13 +17,18 @@ package objects import ( "io" + "github.com/go-air/pal/internal/plain" "github.com/go-air/pal/memory" ) type Array struct { object - elemSize int - n int + elemSize int64 + n int64 +} + +func (a *Array) Len() int64 { + return a.n } func (a *Array) At(i int) memory.Loc { @@ -33,11 +38,30 @@ func (a *Array) At(i int) memory.Loc { func (a *Array) PlainEncode(w io.Writer) error { var err error - err = a.loc.PlainEncode(w) - return err + err = a.object.plainEncode(w) + if err != nil { + return err + } + _, err = w.Write([]byte{byte(' ')}) + if err != nil { + return err + } + err = plain.EncodeInt64(w, a.elemSize) + if err != nil { + return err + } + _, err = w.Write([]byte{byte(' ')}) + if err != nil { + return err + } + return plain.EncodeInt64(w, a.n) } func (a *Array) PlainDecode(r io.Reader) error { - p := &a.loc - return p.PlainDecode(r) + o := &a.object + err := o.plainDecode(r) + if err != nil { + return err + } + return nil } diff --git a/objects/builder.go b/objects/builder.go index 0a17322..b8197ab 100644 --- a/objects/builder.go +++ b/objects/builder.go @@ -28,21 +28,50 @@ type Builder struct { indexing indexing.T mmod *memory.Model ts *typeset.TypeSet - objects []Object + omap map[memory.Loc]Object start memory.Loc memGen *memory.GenParams } -func NewBuilder(pkgPath string, ind indexing.T, mem *memory.Model, ts *typeset.TypeSet) *Builder { +func NewBuilder(pkgPath string, ind indexing.T) *Builder { b := &Builder{} b.pkgPath = pkgPath b.indexing = ind - b.mmod = mem - b.ts = ts - b.memGen = memory.NewGenParams(ts) + b.mmod = memory.NewModel(ind) + b.ts = typeset.New() + b.memGen = memory.NewGenParams(b.ts) + b.omap = make(map[memory.Loc]Object) return b } +func (b *Builder) Memory() *memory.Model { + return b.mmod +} + +func (b *Builder) TypeSet() *typeset.TypeSet { + return b.ts +} + +func (b *Builder) AddPointsTo(ptr, obj memory.Loc) { + b.mmod.AddPointsTo(ptr, obj) +} + +func (b *Builder) AddLoad(dst, src memory.Loc) { + b.mmod.AddLoad(dst, src) +} + +func (b *Builder) AddStore(dst, src memory.Loc) { + b.mmod.AddStore(dst, src) +} + +func (b *Builder) AddTransfer(dst, src memory.Loc) { + b.mmod.AddTransfer(dst, src) +} + +func (b *Builder) AddTransferIndex(dst, src memory.Loc, i indexing.I) { + b.mmod.AddTransferIndex(dst, src, i) +} + func (b *Builder) Pos(pos token.Pos) *Builder { b.memGen.Pos(pos) return b @@ -70,29 +99,31 @@ func (b *Builder) Type(ty typeset.Type) *Builder { func (b *Builder) Struct(gty *types.Struct) *Struct { b.GoType(gty) s := &Struct{} - s.loc = b.mmod.Gen(b.memGen) + s.loc = b.Gen() s.typ = b.mmod.Type(s.loc) - n := memory.Loc(b.mmod.LSize(s.loc)) - s.Fields = make([]memory.Loc, 0, gty.NumFields()) + n := memory.Loc(b.mmod.Lsize(s.loc)) + s.fields = make([]memory.Loc, 0, gty.NumFields()) for i := memory.Loc(1); i < n; i++ { pfloc := b.mmod.Parent(s.loc + i) if pfloc == s.loc { - s.Fields = append(s.Fields, s.loc+i) + s.fields = append(s.fields, s.loc+i) } } - if len(s.Fields) != gty.NumFields() { + if len(s.fields) != gty.NumFields() { panic("internal error") } + b.omap[s.loc] = s return s } func (b *Builder) Array(gty *types.Array) *Array { b.GoType(gty) a := &Array{} - a.loc = b.mmod.Gen(b.memGen) + a.loc = b.Gen() a.typ = b.mmod.Type(a.loc) - a.n = int(gty.Len()) // XXX int v int64 - a.elemSize = b.ts.Lsize(b.ts.Elem(a.typ)) + a.n = gty.Len() + a.elemSize = int64(b.ts.Lsize(b.ts.Elem(a.typ))) + b.omap[a.loc] = a return a } @@ -100,28 +131,96 @@ func (b *Builder) Slice(gty *types.Slice, length, capacity indexing.I) *Slice { ty := b.ts.FromGoType(gty) b.Type(ty) s := &Slice{} + s.loc = b.Gen() s.Len = length s.Cap = capacity + // add one uni-slot by default. + b.AddSlot(s, b.indexing.Var()) + b.omap[s.loc] = s return s } func (b *Builder) AddSlot(slice *Slice, i indexing.I) { elem := b.ts.Elem(slice.typ) - b.memGen.Type(elem) - slice.Slots = append(slice.Slots, Slot{ - Loc: b.mmod.Gen(b.memGen), + slice.slots = append(slice.slots, Slot{ + Loc: b.Type(elem).Gen(), I: i}) } func (b *Builder) Map(gty *types.Map) *Map { ty := b.ts.FromGoType(gty) kty, ety := b.ts.Key(ty), b.ts.Elem(ty) - mloc := b.mmod.Gen(b.memGen.Type(typeset.UnsafePointer)) - kloc := b.mmod.Gen(b.memGen.Type(kty)) - eloc := b.mmod.Gen(b.memGen.Type(ety)) + mloc := b.mmod.Gen(b.memGen.Type(ty)) + kloc := b.Type(kty).Gen() + eloc := b.Type(ety).Gen() + b.mmod.AddPointsTo(mloc, eloc) - m := &Map{Key: kloc, Elem: eloc} + m := &Map{key: kloc, elem: eloc} m.loc = mloc m.typ = ty + b.omap[m.loc] = m return m } + +func (b *Builder) Object(m memory.Loc) Object { + return b.omap[m] +} + +func (b *Builder) Gen() memory.Loc { + return b.mmod.Gen(b.memGen) +} + +func (b *Builder) WithPointer() (obj, ptr memory.Loc) { + obj, ptr = b.mmod.WithPointer(b.memGen) + return +} + +func (b *Builder) Pointer(gtype *types.Pointer) *Pointer { + ptr := &Pointer{} + ptr.typ = b.ts.FromGoType(gtype) + ptr.loc = b.Type(ptr.typ).Gen() + b.omap[ptr.loc] = ptr + return ptr +} + +// Func makes a function object. It is for top level functions +// which may or may not be declared. `declName` must be empty +// iff the associated function is not declared. +func (b *Builder) Func(sig *types.Signature, declName string, opaque memory.Attrs) *Func { + fn := &Func{declName: declName} + fn.typ = b.ts.FromGoType(sig) + fn.loc = b.Type(fn.typ).Gen() + + fn.params = make([]memory.Loc, sig.Params().Len()) + fn.variadic = sig.Variadic() + fn.results = make([]memory.Loc, sig.Results().Len()) + + b.Class(memory.Local) // for all params and returns + + recv := sig.Recv() + + if recv != nil { + b.memGen.Type(b.ts.FromGoType(recv.Type())) + fn.recv = b.mmod.Gen(b.memGen) + } + params := sig.Params() + N := params.Len() + for i := 0; i < N; i++ { + param := params.At(i) + pty := b.ts.FromGoType(param.Type()) + fn.params[i] = + b.Pos(param.Pos()).Type(pty).Attrs(memory.IsParam | opaque).Gen() + } + rets := sig.Results() + N = rets.Len() + for i := 0; i < N; i++ { + ret := rets.At(i) + rty := b.ts.FromGoType(ret.Type()) + fn.results[i] = + b.Pos(ret.Pos()).Type(rty).Attrs(memory.IsReturn | opaque).Gen() + } + // TBD: FreeVars + b.omap[fn.loc] = fn + return fn + +} diff --git a/objects/chan.go b/objects/chan.go index 760d8e6..4b43583 100644 --- a/objects/chan.go +++ b/objects/chan.go @@ -14,6 +14,17 @@ package objects +import "io" + type Chan struct { object } + +func (c *Chan) PlainEncode(w io.Writer) error { + return nil +} + +func (c *Chan) PlainDecode(r io.Reader) error { + return nil +} + diff --git a/objects/func.go b/objects/func.go index 62dc8b2..257eec1 100644 --- a/objects/func.go +++ b/objects/func.go @@ -15,11 +15,9 @@ package objects import ( - "go/token" - "go/types" + "io" "github.com/go-air/pal/memory" - "github.com/go-air/pal/results" "github.com/go-air/pal/typeset" ) @@ -34,52 +32,6 @@ type Func struct { variadic bool } -func NewFunc(bld *results.Builder, sig *types.Signature, declName string) *Func { - fn := &Func{declName: declName} - bld.Reset() - bld.Class = memory.Global - bld.Pos = token.NoPos // XXX - bld.Type = sig - fn.loc = bld.GenLoc() - bld.GenPointsTo(fn.loc, fn.loc) - fn.params = make([]memory.Loc, sig.Params().Len()) - fn.results = make([]memory.Loc, sig.Results().Len()) - - // handle parameters - opaque := memory.NoAttrs - if token.IsExported(declName) { - opaque |= memory.IsOpaque - } - bld.Class = memory.Local // always - - recv := sig.Recv() - if recv != nil { - bld.Class = memory.Local - fn.recv = bld.GenLoc() - } - fn.variadic = sig.Variadic() - params := sig.Params() - N := params.Len() - for i := 0; i < N; i++ { - param := params.At(i) - bld.Pos = param.Pos() - bld.Type = param.Type() - bld.Attrs = memory.IsParam | opaque - fn.params[i] = bld.GenLoc() - } - rets := sig.Results() - N = rets.Len() - for i := 0; i < N; i++ { - ret := rets.At(i) - bld.Pos = ret.Pos() - bld.Type = ret.Type() - bld.Attrs = memory.IsReturn | opaque - fn.results[i] = bld.GenLoc() - } - // TBD: FreeVars - return fn -} - func (f *Func) Declared() bool { return f.declName != "" } @@ -112,3 +64,11 @@ func (f *Func) ResultLoc(i int) memory.Loc { func (f *Func) NResults() int { return len(f.results) } + +func (f *Func) PlainEncode(w io.Writer) error { + return nil +} + +func (f *Func) PlainDecode(r io.Reader) error { + return nil +} diff --git a/objects/interface.go b/objects/interface.go index a6b35f8..f540d20 100644 --- a/objects/interface.go +++ b/objects/interface.go @@ -14,9 +14,21 @@ package objects -import "github.com/go-air/pal/memory" +import ( + "io" + + "github.com/go-air/pal/memory" +) type Interface struct { object Concrete memory.Loc } + +func (i *Interface) PlainEncode(w io.Writer) error { + return nil +} + +func (i *Interface) PlainDecode(r io.Reader) error { + return nil +} diff --git a/objects/map.go b/objects/map.go index bd7124a..9c19718 100644 --- a/objects/map.go +++ b/objects/map.go @@ -14,10 +14,30 @@ package objects -import "github.com/go-air/pal/memory" +import ( + "io" + + "github.com/go-air/pal/memory" +) type Map struct { object - Key memory.Loc - Elem memory.Loc + key memory.Loc + elem memory.Loc +} + +func (m *Map) Key() memory.Loc { + return m.key +} + +func (m *Map) Elem() memory.Loc { + return m.elem +} + +func (m *Map) PlainEncode(w io.Writer) error { + return nil +} + +func (m *Map) PlainDecode(r io.Reader) error { + return nil } diff --git a/objects/object.go b/objects/object.go index 569e828..31cab54 100644 --- a/objects/object.go +++ b/objects/object.go @@ -15,6 +15,8 @@ package objects import ( + "io" + "github.com/go-air/pal/internal/plain" "github.com/go-air/pal/memory" "github.com/go-air/pal/typeset" @@ -33,3 +35,11 @@ type object struct { func (o *object) Loc() memory.Loc { return o.loc } func (o *object) Type() typeset.Type { return o.typ } + +func (o *object) plainEncode(w io.Writer) error { + return plain.EncodeJoin(w, " ", o.loc, o.typ) +} + +func (o *object) plainDecode(r io.Reader) error { + return plain.DecodeJoin(r, " ", &o.loc, &o.typ) +} diff --git a/objects/pointer.go b/objects/pointer.go index 928e134..a1f192e 100644 --- a/objects/pointer.go +++ b/objects/pointer.go @@ -14,6 +14,16 @@ package objects +import "io" + type Pointer struct { object } + +func (p *Pointer) PlainEncode(w io.Writer) error { + return nil +} + +func (p *Pointer) PlainDecode(r io.Reader) error { + return nil +} diff --git a/objects/slice.go b/objects/slice.go index 5d55c4b..f04eec5 100644 --- a/objects/slice.go +++ b/objects/slice.go @@ -15,6 +15,8 @@ package objects import ( + "io" + "github.com/go-air/pal/indexing" "github.com/go-air/pal/memory" ) @@ -23,10 +25,34 @@ type Slice struct { object Len indexing.I Cap indexing.I - Slots []Slot + slots []Slot +} + +func (slice *Slice) NumSlots() int { + return len(slice.slots) +} + +func (slice *Slice) Slot(i int) Slot { + return slice.slots[i] } type Slot struct { I indexing.I Loc memory.Loc } + +func (slot *Slot) PlainEncode(w io.Writer) error { + return nil +} + +func (slot *Slot) PlainDecode(r io.Reader) error { + return nil +} + +func (slice *Slice) PlainEncode(w io.Writer) error { + return nil +} + +func (slice *Slice) PlainDecode(r io.Reader) error { + return nil +} diff --git a/objects/struct.go b/objects/struct.go index 9dedff6..bee07c0 100644 --- a/objects/struct.go +++ b/objects/struct.go @@ -14,9 +14,29 @@ package objects -import "github.com/go-air/pal/memory" +import ( + "io" + + "github.com/go-air/pal/memory" +) type Struct struct { object - Fields []memory.Loc + fields []memory.Loc +} + +func (s *Struct) NumFields(i int) int { + return len(s.fields) +} + +func (s *Struct) Field(i int) memory.Loc { + return s.fields[i] +} + +func (s *Struct) PlainEncode(w io.Writer) error { + return nil +} + +func (s *Struct) PlainDecode(r io.Reader) error { + return nil } diff --git a/results/builder.go b/results/builder.go index 5f4c908..8f08e97 100644 --- a/results/builder.go +++ b/results/builder.go @@ -84,7 +84,3 @@ func (b *Builder) GenTransferIndex(dst, src memory.Loc, off indexing.I) { func (b *Builder) Model() *memory.Model { return b.mdl } - -func (b *Builder) Check() error { - return b.mdl.Check() -} diff --git a/ssa2pal/t.go b/ssa2pal/t.go index a527ad1..fdb09c6 100644 --- a/ssa2pal/t.go +++ b/ssa2pal/t.go @@ -26,7 +26,6 @@ import ( "sort" "github.com/go-air/pal/indexing" - "github.com/go-air/pal/internal/plain" "github.com/go-air/pal/memory" "github.com/go-air/pal/objects" "github.com/go-air/pal/results" @@ -46,11 +45,10 @@ type T struct { indexing indexing.T results *results.T pkgres *results.PkgRes - buildr *results.Builder // map from ssa.Value to memory locs - vmap map[ssa.Value]memory.Loc - objects []objects.Object + vmap map[ssa.Value]memory.Loc + buildr *objects.Builder funcs map[*ssa.Function]*objects.Func } @@ -78,7 +76,7 @@ func New(pass *analysis.Pass, vs indexing.T) (*T, error) { results: palres, pkgres: pkgRes, indexing: vs, - buildr: results.NewBuilder(pkgRes), + buildr: objects.NewBuilder(pkgPath, vs), vmap: make(map[ssa.Value]memory.Loc, 8192), funcs: make(map[*ssa.Function]*objects.Func)} @@ -103,8 +101,7 @@ func (p *T) GenResult() (*results.T, error) { mbr := mbrs[name] switch g := mbr.(type) { case *ssa.Global: - p.buildr.Reset() - p.genGlobal(p.buildr, name, g) + p.genGlobal(name, g) } } @@ -113,8 +110,7 @@ func (p *T) GenResult() (*results.T, error) { mbr := mbrs[name] switch fn := mbr.(type) { case *ssa.Function: - p.buildr.Reset() - if err = p.addFuncDecl(p.buildr, name, fn); err != nil { + if err = p.addFuncDecl(name, fn); err != nil { return nil, err } } @@ -126,48 +122,36 @@ func (p *T) GenResult() (*results.T, error) { return p.results, nil } -func (p *T) genGlobal(buildr *results.Builder, name string, x *ssa.Global) { +func (p *T) genGlobal(name string, x *ssa.Global) { // globals are in general pointers to the globally stored // index - buildr.Pos = x.Pos() - buildr.Class = memory.Global + p.buildr.Pos(x.Pos()).Class(memory.Global).Attrs(memory.NoAttrs) if token.IsExported(name) { // mark opaque because packages which import this one // may set the variable to whatever. - buildr.Attrs = memory.IsOpaque + p.buildr.Attrs(memory.IsOpaque) } switch ty := x.Type().Underlying().(type) { case *types.Pointer: - buildr.Type = ty.Elem() - - loc, ptr := buildr.GenWithPointer() - p.vmap[x] = ptr - if traceLocVal { - fmt.Printf("g %s %s %s %v %p\n", x.Name(), plain.String(loc), buildr.Type, x, x) - } - - return - + p.vmap[x] = p.buildr.Pointer(ty).Loc() default: msg := fmt.Sprintf( "unexpected ssa global member type for %s %T\n", name, - buildr.Type) + x.Type().Underlying()) + panic(msg) } } -func (p *T) addFuncDecl(bld *results.Builder, name string, fn *ssa.Function) error { - if fn.Signature.Variadic() { - // XXX(wsc) - return fmt.Errorf( - "warning: \"%s\".%s: variadic params not yet handled\n", - p.PkgPath(), - name) +func (p *T) addFuncDecl(name string, fn *ssa.Function) error { + opaque := memory.NoAttrs + if token.IsExported(name) { + opaque = memory.IsOpaque } - memFn := objects.NewFunc(bld, fn.Signature, name) + memFn := p.buildr.Func(fn.Signature, name, opaque) + p.vmap[fn] = memFn.Loc() - bld.Reset() for i, param := range fn.Params { p.vmap[param] = memFn.ParamLoc(i) @@ -175,31 +159,31 @@ func (p *T) addFuncDecl(bld *results.Builder, name string, fn *ssa.Function) err // free vars not needed here -- top level func def p.funcs[fn] = memFn - p.genBlocksValues(bld, name, fn) - p.genConstraints(bld, name, fn) + p.genBlocksValues(name, fn) + p.genConstraints(name, fn) return nil } -func (p *T) genBlocksValues(bld *results.Builder, name string, fn *ssa.Function) { +func (p *T) genBlocksValues(name string, fn *ssa.Function) { for _, blk := range fn.Blocks { - p.genBlockValues(bld, name, blk) + p.genBlockValues(name, blk) } if fn.Recover != nil { - p.genBlockValues(bld, name, fn.Recover) + p.genBlockValues(name, fn.Recover) } } -func (p *T) genConstraints(bld *results.Builder, name string, fn *ssa.Function) { +func (p *T) genConstraints(name string, fn *ssa.Function) { for _, blk := range fn.Blocks { - p.genBlockConstraints(bld, name, blk) + p.genBlockConstraints(name, blk) } if fn.Recover != nil { - p.genBlockConstraints(bld, name, fn.Recover) + p.genBlockConstraints(name, fn.Recover) } } -func (p *T) genBlockValues(bld *results.Builder, name string, blk *ssa.BasicBlock) { +func (p *T) genBlockValues(name string, blk *ssa.BasicBlock) { rands := make([]*ssa.Value, 0, 13) for _, i9n := range blk.Instrs { switch v := i9n.(type) { @@ -211,7 +195,7 @@ func (p *T) genBlockValues(bld *results.Builder, name string, blk *ssa.BasicBloc vv := v.(ssa.Value) _, ok := p.vmap[vv] if !ok { - p.genValueLoc(bld, v.(ssa.Value)) + p.genValueLoc(v.(ssa.Value)) } } rands = i9n.Operands(rands[:0]) @@ -220,7 +204,7 @@ func (p *T) genBlockValues(bld *results.Builder, name string, blk *ssa.BasicBloc if !ok && *arg != nil { // I believe that if *arg is nil, the // arg is irrelevant. This does happen. - p.genValueLoc(bld, *arg) + p.genValueLoc(*arg) } } } @@ -230,46 +214,36 @@ func (p *T) genBlockValues(bld *results.Builder, name string, blk *ssa.BasicBloc // // genValueLoc may need to work recursively on struct and // array typed structured data. -func (p *T) genValueLoc(bld *results.Builder, v ssa.Value) memory.Loc { - bld.Reset() - bld.Pos = v.Pos() - bld.Type = v.Type().Underlying() - bld.Class = memory.Local +func (p *T) genValueLoc(v ssa.Value) memory.Loc { + p.buildr.Pos(v.Pos()).GoType(v.Type()).Class(memory.Local).Attrs(memory.NoAttrs) var res memory.Loc switch v := v.(type) { case *ssa.Alloc: - res = p.genAlloc(bld, v) + res = p.buildr.Gen() case *ssa.MakeSlice: - eTy := v.Type().Underlying().(*types.Slice).Elem() - bld.Class = memory.Heap - bld.Type = eTy - _, res = bld.GenWithPointer() + res = p.buildr.Slice(v.Type().(*types.Slice), + p.indexing.Var(), + p.indexing.Var()).Loc() case *ssa.MakeMap: - eTy := v.Type().Underlying().(*types.Map).Elem() - bld.Class = memory.Heap - bld.Type = eTy - _, res = bld.GenWithPointer() + res = p.buildr.Map(v.Type().(*types.Map)).Loc() case *ssa.Field: xloc, ok := p.vmap[v.X] if !ok { - xloc = p.genValueLoc(bld, v.X) + xloc = p.genValueLoc(v.X) // reset bld cfg for genLoc below - bld.Pos = v.Pos() - bld.Type = v.Type().Underlying() - bld.Class = memory.Local + p.buildr.Pos(v.Pos()).GoType(v.Type()).Class(memory.Local).Attrs(memory.NoAttrs) } - res = bld.Field(xloc, v.Field) + res = p.buildr.Object(xloc).(*objects.Struct).Field(v.Field) case *ssa.Index: xloc, ok := p.vmap[v.X] if !ok { - xloc = p.genValueLoc(bld, v.X) + xloc = p.genValueLoc(v.X) // reset bld cfg for genLoc below - bld.Pos = v.Pos() - bld.Type = v.Type().Underlying() - bld.Class = memory.Local + p.buildr.Pos(v.Pos()).GoType(v.Type()).Class(memory.Local).Attrs(memory.NoAttrs) } + x := p.buildr.Object(xloc).(*objects.Array) switch v := v.Index.(type) { case *ssa.Const: i64, ok := constant.Int64Val(v.Value) @@ -277,7 +251,7 @@ func (p *T) genValueLoc(bld *results.Builder, v ssa.Value) memory.Loc { panic("type checked const index not precise as int64") } i := int(i64) // should be ok also b/c it is type checked. - res = bld.ArrayIndex(xloc, i) + res = x.At(i) default: // we have a variable or expression // generate a new loc and transfer @@ -285,18 +259,16 @@ func (p *T) genValueLoc(bld *results.Builder, v ssa.Value) memory.Loc { // TBD: use indexing ty := v.Type().Underlying().(*types.Array) eltTy := ty.Elem() - bld.Type = eltTy - bld.Pos = v.Pos() - res = bld.GenLoc() - N := ty.Len() - for i := int64(0); i < N; i++ { - eltLoc := bld.ArrayIndex(xloc, int(i)) - bld.GenTransfer(res, eltLoc) + res = p.buildr.GoType(eltTy).Gen() + N := int(ty.Len()) + for i := 0; i < N; i++ { + eltLoc := x.At(i) + p.buildr.AddTransfer(res, eltLoc) } } default: - res = bld.GenLoc() + res = p.buildr.Gen() } p.vmap[v] = res return res @@ -305,32 +277,30 @@ func (p *T) genValueLoc(bld *results.Builder, v ssa.Value) memory.Loc { // genAlloc generates a memory.Loc associated with an *ssa.Alloc // we handle these specially because they generate the allocated // object but are associated with pointers to these objects. -func (p *T) genAlloc(bld *results.Builder, a *ssa.Alloc) memory.Loc { - bld.Reset() - bld.Class = memory.Local +func (p *T) genAlloc(a *ssa.Alloc) memory.Loc { if a.Heap { - bld.Class = memory.Global + p.buildr.Class(memory.Global) } - bld.Type = a.Type().Underlying().(*types.Pointer).Elem() - bld.Pos = a.Pos() + p.buildr.GoType(a.Type().(*types.Pointer).Elem()) + p.buildr.Pos(a.Pos()) - _, ptr := bld.GenWithPointer() + _, ptr := p.buildr.WithPointer() return ptr } // generate all constraints for blk // value nodes already have memory.Locs -func (p *T) genBlockConstraints(bld *results.Builder, fnName string, blk *ssa.BasicBlock) error { +func (p *T) genBlockConstraints(fnName string, blk *ssa.BasicBlock) error { for _, i9n := range blk.Instrs { - if err := p.genI9nConstraints(bld, fnName, i9n); err != nil { + if err := p.genI9nConstraints(fnName, i9n); err != nil { return err } } return nil } -func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instruction) error { +func (p *T) genI9nConstraints(fnName string, i9n ssa.Instruction) error { if traceGenI9n { fmt.Printf("gen %s\n", i9n) } @@ -338,13 +308,13 @@ func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instr case *ssa.Alloc: // done in gen locs case *ssa.BinOp: case *ssa.Call: - p.call(bld, i9n.Call) + p.call(i9n.Call) case *ssa.ChangeInterface: case *ssa.ChangeType: case *ssa.Convert: case *ssa.DebugRef: case *ssa.Defer: - p.call(bld, i9n.Call) + p.call(i9n.Call) case *ssa.Extract: case *ssa.Field: // done in gen locs @@ -356,20 +326,20 @@ func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instr ptr := p.vmap[i9n.X] out := p.vmap[i9n] - mdl := bld.Model() + mdl := p.buildr.Memory() obj := mdl.Obj(ptr) fobj := memory.NoLoc if obj != memory.NoLoc { fobj = mdl.Field(obj, i9n.Field) - bld.GenPointsTo(out, fobj) + p.buildr.AddPointsTo(out, fobj) mdl.SetObj(out, fobj) } else { - mdl.AddTransferIndex(out, ptr, p.indexing.FromInt(i9n.Field)) + mdl.AddTransferIndex(out, ptr, p.indexing.FromInt64(int64(i9n.Field))) } case *ssa.Go: // for now, treat as call - p.call(bld, i9n.Call) + p.call(i9n.Call) case *ssa.If: case *ssa.Index: // constraints done in gen locs case *ssa.IndexAddr: @@ -377,9 +347,9 @@ func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instr res := p.vmap[i9n] switch i9n.X.Type().Underlying().(type) { case *types.Pointer: // to array - p.buildr.GenTransfer(res, ptr) + p.buildr.AddTransfer(res, ptr) case *types.Slice: - p.buildr.GenTransfer(res, ptr) + p.buildr.AddTransfer(res, ptr) default: panic("unexpected type of ssa.IndexAddr.X") } @@ -387,9 +357,6 @@ func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instr case *ssa.Jump: case *ssa.Lookup: case *ssa.MakeInterface: - bld.Type = i9n.Type() - bld.Class = memory.Heap - bld.GenLoc() case *ssa.MakeClosure: case *ssa.MakeChan: case *ssa.MakeSlice: // constraints done in genLoc @@ -409,7 +376,7 @@ func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instr v := p.vmap[i9n] for _, x := range i9n.Edges { ev := p.vmap[x] - bld.GenTransfer(v, ev) + p.buildr.AddTransfer(v, ev) } case *ssa.Range: case *ssa.RunDefers: @@ -424,25 +391,25 @@ func (p *T) genI9nConstraints(bld *results.Builder, fnName string, i9n ssa.Instr for i, res := range i9n.Results { resLoc := palFn.ResultLoc(i) vloc := p.vmap[res] - bld.GenTransfer(resLoc, vloc) + p.buildr.AddTransfer(resLoc, vloc) } case *ssa.UnOp: // Load switch i9n.Op { case token.MUL: // *p - bld.GenLoad(p.vmap[i9n], p.vmap[i9n.X]) + p.buildr.AddLoad(p.vmap[i9n], p.vmap[i9n.X]) case token.ARROW: // <- TBD: default: } case *ssa.Slice: - bld.GenTransfer(p.vmap[i9n], p.vmap[i9n.X]) + p.buildr.AddTransfer(p.vmap[i9n], p.vmap[i9n.X]) case *ssa.Store: vloc := p.vmap[i9n.Val] aloc := p.vmap[i9n.Addr] - bld.GenStore(aloc, vloc) + p.buildr.AddStore(aloc, vloc) case *ssa.TypeAssert: default: @@ -455,9 +422,9 @@ func (p *T) PkgPath() string { return p.pass.Pkg.Path() } -func (p *T) call(b *results.Builder, c ssa.CallCommon) { +func (p *T) call(c ssa.CallCommon) { if c.IsInvoke() { - p.invoke(b, c) + p.invoke(c) return } callee := c.StaticCallee() @@ -467,7 +434,7 @@ func (p *T) call(b *results.Builder, c ssa.CallCommon) { } -func (p *T) invoke(b *results.Builder, c ssa.CallCommon) { +func (p *T) invoke(c ssa.CallCommon) { } func (p *T) putResults() { diff --git a/typeset/gotypes.go b/typeset/gotypes.go index 42a5423..76414c1 100644 --- a/typeset/gotypes.go +++ b/typeset/gotypes.go @@ -28,6 +28,8 @@ func (t *TypeSet) FromGoType(gotype types.Type) Type { return Bool case types.Int: return Int64 + case types.Uint: + return Uint64 case types.Int8: return Int8 @@ -60,7 +62,7 @@ func (t *TypeSet) FromGoType(gotype types.Type) Type { case types.Uintptr: return Uintptr default: - panic("untyped const has no pal type") + panic(fmt.Sprintf("%s has no pal type", ty)) } case *types.Slice: @@ -81,10 +83,12 @@ func (t *TypeSet) FromGoType(gotype types.Type) Type { N := ty.NumFields() fields := make([]named, N) + off := 1 for i := 0; i < N; i++ { goField := ty.Field(i) fty := t.FromGoType(goField.Type()) - fields[i] = named{name: goField.Name(), typ: fty} + fields[i] = named{name: goField.Name(), typ: fty, loff: off} + off += t.Lsize(fty) } return t.getStruct(fields) @@ -128,10 +132,13 @@ func (t *TypeSet) FromGoType(gotype types.Type) Type { case *types.Tuple: N := ty.Len() res := make([]named, N) + off := 0 for i := 0; i < N; i++ { at := ty.At(i) res[i].name = at.Name() res[i].typ = t.FromGoType(at.Type()) + res[i].loff = off + off += t.Lsize(res[i].typ) } return t.getTuple(res) diff --git a/typeset/node.go b/typeset/node.go index 0b5cf1f..481de9f 100644 --- a/typeset/node.go +++ b/typeset/node.go @@ -25,8 +25,8 @@ type node struct { elem Type // pointer, array, slice key Type // map keys, method receivers fields []named // struct/interface only - params []named // name == nil ok - results []named // name == nil ok + params []named // name == "" ok + results []named // name == "" ok variadic bool // hashing @@ -38,6 +38,7 @@ type node struct { type named struct { name string typ Type + loff int // 0 when for params or methods } func (n named) PlainEncode(w io.Writer) error { diff --git a/typeset/type.go b/typeset/type.go index fc2ac29..40207a9 100644 --- a/typeset/type.go +++ b/typeset/type.go @@ -14,6 +14,11 @@ package typeset +import ( + "fmt" + "io" +) + type Type uint32 const ( @@ -36,3 +41,14 @@ const ( Uintptr _endType ) + +func (t Type) PlainEncode(w io.Writer) error { + _, err := fmt.Fprintf(w, "%08x", t) + return err + +} + +func (t *Type) PlainDecode(r io.Reader) error { + _, err := fmt.Fscanf(r, "%08x", t) + return err +} diff --git a/typeset/typeset.go b/typeset/typeset.go index 76ce22a..f48c7a2 100644 --- a/typeset/typeset.go +++ b/typeset/typeset.go @@ -46,6 +46,11 @@ func (t *TypeSet) Kind(ty Type) Kind { return t.nodes[ty].kind } +func (t *TypeSet) IsObject(ty Type) bool { + k := t.Kind(ty) + return k != Basic && k != Tuple +} + func (t *TypeSet) Lsize(ty Type) int { return t.nodes[ty].lsize } @@ -62,6 +67,7 @@ func (t *TypeSet) ArrayLen(ty Type) int { node := &t.nodes[ty] n := node.lsize - 1 eltSize := t.nodes[node.elem].lsize + // nb lsize is never 0. if n%eltSize != 0 { panic("bad array len") } @@ -72,9 +78,9 @@ func (t *TypeSet) NumFields(ty Type) int { return len(t.nodes[ty].fields) } -func (t *TypeSet) Field(ty Type, i int) (name string, fty Type) { +func (t *TypeSet) Field(ty Type, i int) (name string, fty Type, loff int) { f := t.nodes[ty].fields[i] - return f.name, f.typ + return f.name, f.typ, f.loff } func (t *TypeSet) Recv(ty Type) Type { @@ -288,6 +294,9 @@ func (t *TypeSet) namedsEqual(as, bs []named) bool { if anamed.name != bnamed.name { return false } + if anamed.loff != bnamed.loff { + return false + } if !t.equal(anamed.typ, bnamed.typ) { return false } diff --git a/typeset/typeset_test.go b/typeset/typeset_test.go index c7ba35d..c844b53 100644 --- a/typeset/typeset_test.go +++ b/typeset/typeset_test.go @@ -30,6 +30,7 @@ func TestTypeSetGrow(t *testing.T) { for i := 0; i < 2025; i++ { palBase = ts.getPointer(palBase) } + //ts.PlainEncode(os.Stdout) if err := plain.TestRoundTrip(ts, false); err != nil { t.Error(err)