Skip to content

Commit

Permalink
first draft: function calls
Browse files Browse the repository at this point in the history
We have implemented function calls in a way which should facilitate
closures and interfaces a great deal.

rather than the traditional Anderson copy/transfer constraints
of arguments to parameters and function results to caller
local results, we

1. represent each function in contiguous memory as a sequence (p0, p1,
   ..., pn, r0, r1, ..., rm)  where each pi is a pointer to
an object of type of parameter i, and each ri is a pointer to an
object of type of return i.
2. upon analyzing a function we take *pi as the starting value of
the parameter.
3. Upon calling a function, we set *pi = argi and resi = *ri.

Now, given 2 functions f, g of the same signature, we have
copy/transfer constraints (fp0, fp1, ..., fpn, fr0, fr1, ..., frm) =
(gp0, gp1, ..., gpn, gr0, gr1, ..., grn).  then the points-to
of each parameter and result are transfered to f from g.

In this way, dynamic dispatch should be a cinch: just call the
function object associated with the node, it will have all
the loads/stores of any function assigned to it.

Something similar should work for interfaces:  we just keep a struct
of function objects defined as above, and on assignment to an interface
value, we assign the functions from the concrete type.

Likewise, this should facilitate closures by adding a free variables
component like the pi,ri components.
  • Loading branch information
scott-cotton committed Aug 27, 2021
1 parent 2d2291b commit 6fbbef6
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 25 deletions.
36 changes: 25 additions & 11 deletions memory/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Model struct {
locs []loc
constraints []Constraint
indexing indexing.T
work []Loc
}

// NewModel generates a new memory model for a package.
Expand Down Expand Up @@ -145,7 +146,17 @@ func (mod *Model) Type(m Loc) typeset.Type {
func (mod *Model) Gen(gp *GenParams) Loc {
var sum int
p := Loc(uint32(len(mod.locs)))
return mod.p_add(gp, p, p, &sum)
result := mod.add(gp, p, p, &sum)
for _, ptr := range mod.work {
gp.typ = gp.ts.Elem(mod.Type(ptr))
sum := 0
p = Loc(uint32(len(mod.locs)))
obj := mod.add(gp, p, p, &sum)
mod.locs[ptr].obj = obj
mod.AddAddressOf(ptr, obj)
}
mod.work = mod.work[:0]
return result
}

func (mod *Model) WithPointer(gp *GenParams) (obj, ptr Loc) {
Expand Down Expand Up @@ -180,12 +191,12 @@ func (mod *Model) Cap(c int) {
mod.locs = mod.locs[:c]
}

// p_add adds a root recursively according to ty.
// add adds a root recursively according to ty.
//
// add is responsible for setting the size, parent, class, attrs, typ, and root
// of all added nodes.
//
func (mod *Model) p_add(gp *GenParams, p, r Loc, sum *int) Loc {
func (mod *Model) add(gp *GenParams, p, r Loc, sum *int) Loc {
n := Loc(uint32(len(mod.locs)))
l := loc{
parent: p,
Expand Down Expand Up @@ -214,7 +225,7 @@ func (mod *Model) p_add(gp *GenParams, p, r Loc, sum *int) Loc {
elemTy := gp.ts.Elem(gp.typ)
for i := 0; i < m; i++ {
gp.typ = elemTy
mod.p_add(gp, n, r, sum)
mod.add(gp, n, r, sum)
}
case typeset.Struct:
mod.locs = append(mod.locs, l)
Expand All @@ -223,7 +234,7 @@ func (mod *Model) p_add(gp *GenParams, p, r Loc, sum *int) Loc {
styp := gp.typ
for i := 0; i < nf; i++ {
_, gp.typ, _ = gp.ts.Field(styp, i)
mod.p_add(gp, n, r, sum)
mod.add(gp, n, r, sum)
}

case typeset.Tuple:
Expand All @@ -233,31 +244,34 @@ func (mod *Model) p_add(gp *GenParams, p, r Loc, sum *int) Loc {
ttyp := gp.typ
for i := 0; i < tn; i++ {
_, gp.typ, _ = gp.ts.Field(ttyp, i)
mod.p_add(gp, n, r, sum)
mod.add(gp, n, r, sum)
}
case typeset.Named:
gp.typ = gp.ts.Underlying(gp.typ)
mod.p_add(gp, p, r, sum)
mod.add(gp, p, r, sum)
added = false
case typeset.Func:
mod.locs = append(mod.locs, l)
*sum++
fty := gp.typ
rcvty := gp.ts.Recv(fty)
if rcvty != typeset.NoType {
gp.typ = rcvty
mod.p_add(gp, n, r, sum)
gp.typ = gp.ts.PointerTo(rcvty)
mod.work = append(mod.work, mod.add(gp, n, r, sum))
}
// TBD FreeVars

np := gp.ts.NumParams(fty)
for i := 0; i < np; i++ {
_, gp.typ = gp.ts.Param(fty, i)
mod.p_add(gp, n, r, sum)
gp.typ = gp.ts.PointerTo(gp.typ)
mod.work = append(mod.work, mod.add(gp, n, r, sum))
}
nr := gp.ts.NumResults(fty)
for i := 0; i < nr; i++ {
_, gp.typ = gp.ts.Result(fty, i)
mod.p_add(gp, n, r, sum)
gp.typ = gp.ts.PointerTo(gp.typ)
mod.work = append(mod.work, mod.add(gp, n, r, sum))
}

default:
Expand Down
51 changes: 51 additions & 0 deletions objects/builder_call.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2021 The pal authors (see AUTHORS)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package objects

import (
"fmt"

"github.com/go-air/pal/memory"
)

// dst is the memory loc of the result.
// - if f returns a single value, then that
// - otherwise it is a tuple.
//
// args indicate the arguments at the call site.
func (b *Builder) Call(f *Func, dst memory.Loc, args []memory.Loc) {
fmt.Printf("n args %d n params %d\n", len(args), len(f.params))
start := 0
if f.recv != memory.NoLoc {
start = 1
b.AddStore(f.recv, args[0])
}
for i, arg := range args[start:] {
b.AddStore(f.params[i], arg)
}
if dst == memory.NoLoc {
return
}
switch len(f.results) {
case 0:
case 1:
b.AddLoad(dst, f.results[0])
default:
dstTuple := b.omap[dst].(*Tuple)
for i, ret := range f.results {
b.AddLoad(dstTuple.At(i), ret)
}
}
}
2 changes: 1 addition & 1 deletion ssa2pal/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
traceFunc = true
traceParam = false

debugLogModel = true
debugLogModel = false
traceGenValueLoc = false
traceGenExtract = false
traceGenNext = false
Expand Down
45 changes: 32 additions & 13 deletions ssa2pal/t.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -159,13 +158,13 @@ func (p *T) addFuncDecl(name string, fn *ssa.Function) error {
memFn := p.buildr.Func(fn.Signature, name, opaque)

p.vmap[fn] = memFn.Loc()
fmt.Printf("built func %s at %d\n", name, memFn.Loc())

for i, param := range fn.Params {
p.vmap[param] = p.buildr.Memory().Obj(memFn.ParamLoc(i))
if traceParam {
fmt.Printf("setting param %s to %s\n", param, plain.String(memFn.ParamLoc(i)))

fmt.Printf("setting param %s to %s\n", param, p.vmap[param])
}
p.vmap[param] = memFn.ParamLoc(i)
}
// free vars not needed here -- top level func def

Expand Down Expand Up @@ -335,6 +334,7 @@ func (p *T) genValueLoc(v ssa.Value) memory.Loc {
rxloc := p.vmap[iter.X]
if rxloc == memory.NoLoc {
rxloc = p.genValueLoc(iter.X)
fmt.Printf("gen value for %s iter.X of next\n", rxloc)
p.buildr.Pos(v.Pos()).Class(memory.Local).Attrs(memory.NoAttrs)
}
mgoty := iter.X.Type().Underlying().(*types.Map)
Expand Down Expand Up @@ -408,13 +408,13 @@ func (p *T) genI9nConstraints(fnName string, i9n ssa.Instruction) error {

}
case *ssa.Call:
p.call(i9n.Call)
p.call(i9n.Call, p.vmap[i9n])
case *ssa.ChangeInterface:
case *ssa.ChangeType:
case *ssa.Convert:
case *ssa.DebugRef:
case *ssa.Defer:
p.call(i9n.Call)
p.call(i9n.Call, memory.NoLoc)
case *ssa.Extract: // done in gen locs
case *ssa.Field: // done in gen locs

Expand All @@ -439,7 +439,7 @@ func (p *T) genI9nConstraints(fnName string, i9n ssa.Instruction) error {

case *ssa.Go:
// for now, treat as call
p.call(i9n.Call)
p.call(i9n.Call, memory.NoLoc)
case *ssa.If:
case *ssa.Index: // constraints done in gen locs
case *ssa.IndexAddr:
Expand Down Expand Up @@ -503,9 +503,10 @@ func (p *T) genI9nConstraints(fnName string, i9n ssa.Instruction) error {
palFn = p.funcs[ssaFn]
// copy results to palFn results...
for i, res := range i9n.Results {
resLoc := palFn.ResultLoc(i)
resptr := palFn.ResultLoc(i)
resobj := p.buildr.Memory().Obj(resptr)
vloc := p.vmap[res]
p.buildr.AddTransfer(resLoc, vloc)
p.buildr.AddTransfer(resobj, vloc)
}

case *ssa.UnOp:
Expand All @@ -517,9 +518,10 @@ func (p *T) genI9nConstraints(fnName string, i9n ssa.Instruction) error {
c := p.buildr.Object(p.vmap[i9n.X]).(*objects.Chan)
dst := p.vmap[i9n]
if dst == memory.NoLoc {
panic("no loc for <-")
//panic(fmt.Sprintf("no loc for <- %#v\n", i9n))
} else {
c.Recv(dst, p.buildr.Memory())
}
c.Recv(dst, p.buildr.Memory())

default: // indexing
}
Expand All @@ -542,7 +544,7 @@ func (p *T) PkgPath() string {
return p.pass.Pkg.Path()
}

func (p *T) call(c ssa.CallCommon) {
func (p *T) call(c ssa.CallCommon, dst memory.Loc) {
if c.IsInvoke() {
p.invoke(c)
return
Expand All @@ -551,7 +553,24 @@ func (p *T) call(c ssa.CallCommon) {
if callee == nil {
return
}

switch fssa := c.Value.(type) {
case *ssa.Function:
floc := p.vmap[fssa]
if floc == memory.NoLoc {
panic("wilma!")
}
fn := p.buildr.Object(floc).(*objects.Func)
fmt.Printf("calling '%s' loc %d\n", fssa.Name(), floc)
args := make([]memory.Loc, len(c.Args))
for i, argVal := range c.Args {
args[i] = p.vmap[argVal]
}
p.buildr.Call(fn, dst, args)
case *ssa.Builtin:
case *ssa.MakeClosure:
default:
// dynamic dispatch
}
}

func (p *T) invoke(c ssa.CallCommon) {
Expand Down

0 comments on commit 6fbbef6

Please sign in to comment.