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 26 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
1 change: 1 addition & 0 deletions gnovm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ _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)

########################################
# Code gen
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
2 changes: 1 addition & 1 deletion gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1628,7 +1628,7 @@

func (m *Machine) PushBlock(b *Block) {
if debug {
m.Println("+B")
m.Printf("+B: %v \n", b)

Check warning on line 1631 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L1631

Added line #L1631 was not covered by tests
}
m.Blocks = append(m.Blocks, b)
}
Expand Down
20 changes: 11 additions & 9 deletions gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -873,15 +873,17 @@ type SwitchClauseStmt struct {
// 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
Active Stmt // for PopStmt()
Key Expr // for RangeStmt
Value Expr // for RangeStmt
Expand Down
2 changes: 2 additions & 0 deletions gnovm/pkg/gnolang/op_assign.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ func (m *Machine) doOpDefine() {
// 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 @@ -30,6 +31,7 @@ func (m *Machine) doOpAssign() {
// 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
41 changes: 39 additions & 2 deletions gnovm/pkg/gnolang/op_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,44 @@
pts := ft.Params
numParams := len(pts)
isMethod := 0 // 1 if true
// Create new block scope.
clo := fr.Func.GetClosure(m.Store)

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
for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block
box := loopData.loopValuesBox
if box.isSealed {
for _, t := range box.transient { // unpack vars belong to a specific block
if debug {
for k, v := range t.values {
fmt.Printf("values[%d] is %v \n", k, v)
}

Check warning on line 69 in gnovm/pkg/gnolang/op_call.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_call.go#L62-L69

Added lines #L62 - L69 were not covered by tests
}
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

Check warning on line 80 in gnovm/pkg/gnolang/op_call.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_call.go#L71-L80

Added lines #L71 - L80 were not covered by tests
}
}
if match {
// update values in context with previously captured.
loopBlock.UpdateValue(index, t.values[loopData.index])
}

Check warning on line 86 in gnovm/pkg/gnolang/op_call.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_call.go#L83-L86

Added lines #L83 - L86 were not covered by tests
}
} else { // not sealed will be tainted, indicates not sealed with values when loopBlock ends, it's not a closure.
fv.TransientLoopData[i].loopValuesBox.isTainted = true
}

Check warning on line 90 in gnovm/pkg/gnolang/op_call.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_call.go#L88-L90

Added lines #L88 - L90 were not covered by tests
}
fv.TransientLoopData = nil

Check warning on line 92 in gnovm/pkg/gnolang/op_call.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_call.go#L92

Added line #L92 was not covered by tests
}

b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo)
m.PushBlock(b)
if fv.nativeBody == nil && fv.NativePkg != "" {
Expand All @@ -67,6 +103,7 @@
}
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
34 changes: 34 additions & 0 deletions gnovm/pkg/gnolang/op_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@

*/

func updateCapturedValue(m *Machine, lb *Block) {
if lb.GetBodyStmt().isLoop { // do we need this?
bs := lb.GetBodyStmt()
if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted {
var isSealed bool
for i, tt := range bs.LoopValuesBox.transient {
nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name)
ptr := m.LastBlock().GetPointerTo(m.Store, nvp)
tv := ptr.Deref()
// update context use previously recorded value
// inner loops iterates twice while outer loop iterates once.
// it's an alignment for the values of out loop block.
// TODO: hard to understand, more doc or e.g.
expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0
for j := 0; j < expandRatio; j++ {
bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv)
}
isSealed = true

Check warning on line 63 in gnovm/pkg/gnolang/op_exec.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_exec.go#L50-L63

Added lines #L50 - L63 were not covered by tests
}
if isSealed {
bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox for closure execution.
}

Check warning on line 67 in gnovm/pkg/gnolang/op_exec.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_exec.go#L65-L67

Added lines #L65 - L67 were not covered by tests
}
}
}

//----------------------------------------
// doOpExec
//
Expand Down Expand Up @@ -85,6 +111,7 @@
return
}
case OpForLoop:
lb := m.LastBlock()
bs := m.LastBlock().GetBodyStmt()
// evaluate .Cond.
if bs.NextBodyIndex == -2 { // init
Expand Down Expand Up @@ -114,6 +141,7 @@
s = next
goto EXEC_SWITCH
} else if bs.NextBodyIndex == bs.BodyLen {
updateCapturedValue(m, lb)
// (queue to) go back.
if bs.Cond != nil {
m.PushExpr(bs.Cond)
Expand Down Expand Up @@ -206,6 +234,8 @@
s = next // switch on bs.Active
goto EXEC_SWITCH
} else if bs.NextBodyIndex == bs.BodyLen {
// update captured values
updateCapturedValue(m, m.LastBlock())
if bs.ListIndex < bs.ListLen-1 {
// set up next assign if needed.
switch bs.Op {
Expand Down Expand Up @@ -300,6 +330,7 @@
s = next // switch on bs.Active
goto EXEC_SWITCH
} else if bs.NextBodyIndex == bs.BodyLen {
updateCapturedValue(m, m.LastBlock())
if bs.StrIndex < bs.StrLen {
// set up next assign if needed.
switch bs.Op {
Expand Down Expand Up @@ -393,6 +424,7 @@
s = next // switch on bs.Active
goto EXEC_SWITCH
} else if bs.NextBodyIndex == bs.BodyLen {
updateCapturedValue(m, m.LastBlock())

Check warning on line 427 in gnovm/pkg/gnolang/op_exec.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/op_exec.go#L427

Added line #L427 was not covered by tests
nnext := bs.NextItem.Next
if nnext == nil {
// done with range.
Expand Down Expand Up @@ -501,6 +533,7 @@
NextBodyIndex: -2,
Cond: cs.Cond,
Post: cs.Post,
isLoop: true,
}
m.PushBlock(b)
m.PushOp(OpForLoop)
Expand Down Expand Up @@ -587,6 +620,7 @@
Key: cs.Key,
Value: cs.Value,
Op: cs.Op,
isLoop: true,
}
m.PushBlock(b)
// TODO: replace with "cs.Op".
Expand Down
Loading
Loading