Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: capture transient values in loop block for closures #1585

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/gno.land/p/demo/avl/z_0_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ func main() {
// "PkgPath": "gno.land/r/test"
// }
// },
// "TransientLoopData": null,
// "Type": {
// "@type": "/gno.FuncType",
// "Params": [],
Expand Down Expand Up @@ -316,6 +317,7 @@ func main() {
// "PkgPath": "gno.land/r/test"
// }
// },
// "TransientLoopData": null,
// "Type": {
// "@type": "/gno.FuncType",
// "Params": [],
Expand Down
2 changes: 2 additions & 0 deletions examples/gno.land/p/demo/avl/z_1_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ func main() {
// "PkgPath": "gno.land/r/test"
// }
// },
// "TransientLoopData": null,
// "Type": {
// "@type": "/gno.FuncType",
// "Params": [],
Expand Down Expand Up @@ -340,6 +341,7 @@ func main() {
// "PkgPath": "gno.land/r/test"
// }
// },
// "TransientLoopData": null,
// "Type": {
// "@type": "/gno.FuncType",
// "Params": [],
Expand Down
2 changes: 2 additions & 0 deletions gnovm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ _test.gnolang.native:; go test tests/*.go -test.short -run "TestFilesNativ
_test.gnolang.stdlibs:; go test tests/*.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS)
_test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests $(GOTEST_FLAGS)
_test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS)
_test.gnolang.debug:; go test tests/*.go -test.short -run 'TestDebug$$/' --update-golden-tests $(GOTEST_FLAGS)

_test.gnolang.sort:; go test tests/*.go -run "TestPackages/(sort)" $(GOTEST_FLAGS)
########################################
# Code gen
# TODO: move _dev.stringer to go:generate instructions, simplify generate
Expand Down
1 change: 1 addition & 0 deletions gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func main() {
// "PkgPath": "gno.land/r/x"
// }
// },
// "TransientLoopData": null,
// "Type": {
// "@type": "/gno.FuncType",
// "Params": [],
Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ stderr '=== RUN file/x_filetest.gno'
stderr 'panic: fail on x_filetest.gno: diff:'
stderr '--- Expected'
stderr '\+\+\+ Actual'
stderr '@@ -1 \+1,66 @@'
stderr '@@ -1 \+1,67 @@'
stderr '-xxx'
stderr '\+switchrealm\["gno.land/r/x"\]'

Expand Down
1 change: 1 addition & 0 deletions gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func main() {
// "PkgPath": "gno.land/r/x"
// }
// },
// "TransientLoopData": null,
// "Type": {
// "@type": "/gno.FuncType",
// "Params": [],
Expand Down
1 change: 1 addition & 0 deletions gnovm/pkg/gnolang/alloc.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ func (alloc *Allocator) NewMap(size int) *MapValue {
}

func (alloc *Allocator) NewBlock(source BlockNode, parent *Block) *Block {
debug.Printf("---NewBlock, source: %v, parent: %v \n", source, parent)
alloc.AllocateBlock(int64(source.GetNumNames()))
return NewBlock(source, parent)
}
Expand Down
4 changes: 2 additions & 2 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ func (m *Machine) Eval(x Expr) []TypedValue {
// static types and values.
func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue {
if debug {
m.Printf("Machine.EvalStatic(%v, %v)\n", last, x)
//m.Printf("Machine.EvalStatic(%v, %v)\n", last, x)
}
// X must have been preprocessed.
if x.GetAttribute(ATTR_PREPROCESSED) == nil {
Expand Down Expand Up @@ -1628,7 +1628,7 @@ func (m *Machine) ReapValues(start int) []TypedValue {

func (m *Machine) PushBlock(b *Block) {
if debug {
m.Println("+B")
m.Printf("+B: %v \n", b)
}
m.Blocks = append(m.Blocks, b)
}
Expand Down
70 changes: 58 additions & 12 deletions gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,25 @@ type KeyValueExprs []KeyValueExpr
type FuncLitExpr struct {
Attributes
StaticBlock
Type FuncTypeExpr // function type
Body // function body
Type FuncTypeExpr // function type
Body // function body
TransientLoopData []*LoopBlockData
}

func (fx *FuncLitExpr) GetCapturedNxs(store Store) (captures []*NameExpr) {
for _, name := range fx.GetExternNames() {
vp := fx.GetPathForName(store, name)
if vp.Depth == 0 { // skip uverse
continue
}
vp.Depth -= 1 // from the perspective of funcLit block
nx := &NameExpr{
Name: name,
Path: vp,
}
captures = append(captures, nx)
}
return
}

// The preprocessor replaces const expressions
Expand Down Expand Up @@ -870,18 +887,30 @@ type SwitchClauseStmt struct {
// ----------------------------------------
// bodyStmt (persistent)

// this assumes that a goto stmt to label ahead
// forms an implicit loop.
// if an funcLitExpr embeded, do capture staff.
type LoopBlockAttr struct {
isLoop bool
start int // line of label start
end int // line of goto stmt
}

// NOTE: embedded in Block.
type bodyStmt struct {
Attributes
Body // for non-loop stmts
BodyLen int // for for-continue
NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n
NumOps int // number of Ops, for goto
NumValues int // number of Values, for goto
NumExprs int // number of Exprs, for goto
NumStmts int // number of Stmts, for goto
Cond Expr // for ForStmt
Post Stmt // for ForStmt
Body // for non-loop stmts
BodyLen int // for for-continue
NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n
NumOps int // number of Ops, for goto
NumValues int // number of Values, for goto
NumExprs int // number of Exprs, for goto
NumStmts int // number of Stmts, for goto
Cond Expr // for ForStmt
Post Stmt // for ForStmt
LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on
isLoop bool
loopBlockAttr *LoopBlockAttr
Active Stmt // for PopStmt()
Key Expr // for RangeStmt
Value Expr // for RangeStmt
Expand Down Expand Up @@ -1502,6 +1531,7 @@ func (sb *StaticBlock) revertToOld() {

// Implements BlockNode
func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) {
debug.Printf("---init static block, source: %v, parent: %v \n", source, parent)
if sb.Names != nil || sb.Block.Source != nil {
panic("StaticBlock already initialized")
}
Expand All @@ -1512,7 +1542,7 @@ func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) {
Parent: nil,
}
} else {
sb.Block = Block{
sb.Block = Block{ // build Hierarchical structure in preprocess stage
Source: source,
Values: nil,
Parent: parent.GetStaticBlock().GetBlock(),
Expand All @@ -1525,6 +1555,22 @@ func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) {
return
}

func (sb *StaticBlock) dump() {
debug.Println("==============dump staticBlock==============")
for i, n := range sb.Names {
debug.Printf("name[%d] is : %v \n", i, n)
}
for i, c := range sb.Consts {
debug.Printf("const[%d] is : %v \n", i, c)
}

debug.Printf("block.Source: %v \n", sb.Block.Source)
debug.Printf("block.Parent: %v \n", sb.Block.Parent)
debug.Printf("block: %v \n", sb.Block.String())

debug.Println("==============end==============")
}

// Implements BlockNode.
func (sb *StaticBlock) IsInitialized() bool {
return sb.Block.Source != nil
Expand Down
6 changes: 6 additions & 0 deletions gnovm/pkg/gnolang/op_assign.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package gnolang

func (m *Machine) doOpDefine() {
debug.Println("---doOpDefine")
s := m.PopStmt().(*AssignStmt)
debug.Printf("---s: %v \n", s)
// Define each value evaluated for Lhs.
// NOTE: PopValues() returns a slice in
// forward order, not the usual reverse.
// m.PopValue()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete?

rvs := m.PopValues(len(s.Lhs))
lb := m.LastBlock()
for i := 0; i < len(s.Lhs); i++ {
Expand All @@ -25,11 +28,14 @@ func (m *Machine) doOpDefine() {
}

func (m *Machine) doOpAssign() {
debug.Println("---doOpAssign")
s := m.PopStmt().(*AssignStmt)
debug.Printf("---s: %v \n", s)
// Assign each value evaluated for Lhs.
// NOTE: PopValues() returns a slice in
// forward order, not the usual reverse.
rvs := m.PopValues(len(s.Lhs))

for i := len(s.Lhs) - 1; 0 <= i; i-- {
// Pop lhs value and desired type.
lv := m.PopAsPointer(s.Lhs[i])
Expand Down
57 changes: 55 additions & 2 deletions gnovm/pkg/gnolang/op_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (m *Machine) doOpPrecall() {
var gReturnStmt = &ReturnStmt{}

func (m *Machine) doOpCall() {
debug.Println("---doOpCall")
// NOTE: Frame won't be popped until the statement is complete, to
// discard the correct number of results for func calls in ExprStmts.
fr := m.LastFrame()
Expand All @@ -54,8 +55,59 @@ func (m *Machine) doOpCall() {
pts := ft.Params
numParams := len(pts)
isMethod := 0 // 1 if true
// Create new block scope.
clo := fr.Func.GetClosure(m.Store)
debug.Printf("---fv: %v \n", fv)

clo := fr.Func.GetClosure(m.Store) // this is "static"

// update block vars using captured vars
var loopBlock *Block // target loop block with values to be updated
if fv.TransientLoopData != nil { // it captured a bundle of values
debug.Printf("---doOpCall, addr of x.LoopData: %p \n", fv.TransientLoopData)

if debug {
for i, lbd := range fv.TransientLoopData {
fmt.Printf("========doOpCall, TransientLoopData[%d] is: %s, index: %d \n", i, lbd.loopValuesBox, lbd.index)
}
}
for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block
box := loopData.loopValuesBox
if box.isSealed {
debug.Println("---isSealed, do update context")
for _, t := range box.transient { // unpack vars belong to a specific block
if debug {
debug.Printf("---len of values: %d \n", len(t.values))
for k, v := range t.values {
debug.Printf("values[%d] is %v \n", k, v)
}
}
loopBlock = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth
// check if exists, should always be?
names := loopBlock.GetSource(m.Store).GetBlockNames()
var index int
var match bool
for l, n := range names {
if n == t.nx.Name {
index = l
match = true
break
}
}
if match {
// update values in context with previously captured.
loopBlock.UpdateValue(index, t.values[loopData.index])
}
}
} else { // not sealed will be tainted, indicates not sealed with values when loopBlock ends, it's not a closure.
debug.Println("---not sealed, it's tainted")
debug.Printf("---tainted box is: %v \n", fv.TransientLoopData[i].loopValuesBox)
fv.TransientLoopData[i].loopValuesBox.isTainted = true
}
}
fv.TransientLoopData = nil
}

//debug.Println("---no closure ...")

b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo)
m.PushBlock(b)
if fv.nativeBody == nil && fv.NativePkg != "" {
Expand All @@ -67,6 +119,7 @@ func (m *Machine) doOpCall() {
}
if fv.nativeBody == nil {
fbody := fv.GetBodyFromSource(m.Store)
debug.Printf("fbody: %v \n", fbody)
if len(ft.Results) == 0 {
// Push final empty *ReturnStmt;
// TODO: transform in preprocessor instead to return only
Expand Down
2 changes: 1 addition & 1 deletion gnovm/pkg/gnolang/op_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (m *Machine) doOpEval() {
debug.Printf("EVAL: (%T) %v\n", x, x)
// fmt.Println(m.String())
}

// This case moved out of switch for performance.
// TODO: understand this better.
if nx, ok := x.(*NameExpr); ok {
Expand Down Expand Up @@ -307,7 +308,6 @@ func (m *Machine) doOpEval() {
m.PushOp(OpEval)
case *FuncLitExpr:
m.PushOp(OpFuncLit)
// evaluate func type
m.PushExpr(&x.Type)
m.PushOp(OpEval)
case *ConstExpr:
Expand Down
Loading
Loading