Skip to content

Commit

Permalink
compiler: drop stack after inline
Browse files Browse the repository at this point in the history
Some control-flow statements drop stack items, for example
`return` when it is used inside of `range` loop.
For inlined calls this `return` should drop only portion of stack
which belongs to inlined call.
  • Loading branch information
fyrchik committed Mar 1, 2021
1 parent 347212c commit b66b853
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 1 deletion.
9 changes: 8 additions & 1 deletion pkg/compiler/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type codegen struct {
labels map[labelWithType]uint16
// A list of nested label names together with evaluation stack depth.
labelList []labelWithStackSize
// inlineLabelOffsets contains size of labelList at the start of inline call processing.
// For such calls we need to drop only newly created part of stack.
inlineLabelOffsets []int

// A label for the for-loop being currently visited.
currentFor string
Expand Down Expand Up @@ -607,7 +610,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.setLabel(l)

cnt := 0
for i := range c.labelList {
start := 0
if len(c.inlineLabelOffsets) > 0 {
start = c.inlineLabelOffsets[len(c.inlineLabelOffsets)-1]
}
for i := start; i < len(c.labelList); i++ {
cnt += c.labelList[i].sz
}
c.dropItems(cnt)
Expand Down
8 changes: 8 additions & 0 deletions pkg/compiler/inline.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ import (
// <inline body of f directly>
// }
func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
labelSz := len(c.labelList)
offSz := len(c.inlineLabelOffsets)
c.inlineLabelOffsets = append(c.inlineLabelOffsets, labelSz)
defer func() {
c.inlineLabelOffsets = c.inlineLabelOffsets[:offSz]
c.labelList = c.labelList[:labelSz]
}()

pkg := c.buildInfo.program.Package(f.pkg.Path())
sig := c.typeOf(n.Fun).(*types.Signature)

Expand Down
50 changes: 50 additions & 0 deletions pkg/compiler/inline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,56 @@ func TestInline(t *testing.T) {
})
}

func TestInlineInLoop(t *testing.T) {
t.Run("simple", func(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/binary"
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
func Main() int {
sum := 0
values := []int{10, 11}
for _, v := range values {
binary.Itoa(v, 10)
sum += inline.VarSum(1, 2, 3, 4)
}
return sum
}`
eval(t, src, big.NewInt(20))
})
t.Run("check clean stack on return", func(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/binary"
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
func Main() int {
values := []int{10, 11, 12}
for _, v := range values {
binary.Itoa(v, 10)
if v == 11 {
return inline.VarSum(2, 20, 200)
}
}
return 0
}`
eval(t, src, big.NewInt(222))
})
}

func TestInlineInSwitch(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
func Main() int {
switch inline.VarSum(1, 2) {
case inline.VarSum(3, 1):
return 10
case inline.VarSum(4, -1):
return 11
default:
return 12
}
}`
eval(t, src, big.NewInt(11))
}

func TestInlineGlobalVariable(t *testing.T) {
t.Run("simple", func(t *testing.T) {
src := `package foo
Expand Down
8 changes: 8 additions & 0 deletions pkg/compiler/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func newStoragePlugin() *storagePlugin {
s.interops[interopnames.ToID([]byte(interopnames.SystemStoragePut))] = s.Put
s.interops[interopnames.ToID([]byte(interopnames.SystemStorageGetContext))] = s.GetContext
s.interops[interopnames.ToID([]byte(interopnames.SystemRuntimeNotify))] = s.Notify
s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryItoa))] = s.Itoa
return s

}
Expand All @@ -122,6 +123,13 @@ func (s *storagePlugin) syscallHandler(v *vm.VM, id uint32) error {
return errors.New("syscall not found")
}

func (s *storagePlugin) Itoa(v *vm.VM) error {
n := v.Estack().Pop().BigInt()
base := v.Estack().Pop().BigInt()
v.Estack().PushVal(n.Text(int(base.Int64())))
return nil
}

func (s *storagePlugin) Notify(v *vm.VM) error {
name := v.Estack().Pop().String()
item := stackitem.NewArray(v.Estack().Pop().Array())
Expand Down

0 comments on commit b66b853

Please sign in to comment.