Skip to content

Commit

Permalink
Merge pull request #2128 from nspcc-dev/vm-update-int
Browse files Browse the repository at this point in the history
Some VM optimizations
  • Loading branch information
roman-khimov committed Aug 13, 2021
2 parents adc660c 6879f76 commit 5b12dd2
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 219 deletions.
14 changes: 4 additions & 10 deletions pkg/compiler/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -2097,31 2097,25 @@ func (c *codegen) resolveFuncDecls(f *ast.File, pkg *types.Package) {
func (c *codegen) writeJumps(b []byte) ([]byte, error) {
ctx := vm.NewContext(b)
var offsets []int
for op, _, err := ctx.Next(); err == nil && ctx.IP() < len(b); op, _, err = ctx.Next() {
for op, param, err := ctx.Next(); err == nil && ctx.IP() < len(b); op, param, err = ctx.Next() {
switch op {
case opcode.JMP, opcode.JMPIFNOT, opcode.JMPIF, opcode.CALL,
opcode.JMPEQ, opcode.JMPNE,
opcode.JMPGT, opcode.JMPGE, opcode.JMPLE, opcode.JMPLT:
case opcode.TRYL:
nextIP := ctx.NextIP()
catchArg := b[nextIP-8:]
_, err := c.replaceLabelWithOffset(ctx.IP(), catchArg)
_, err := c.replaceLabelWithOffset(ctx.IP(), param)
if err != nil {
return nil, err
}
finallyArg := b[nextIP-4:]
_, err = c.replaceLabelWithOffset(ctx.IP(), finallyArg)
_, err = c.replaceLabelWithOffset(ctx.IP(), param[4:])
if err != nil {
return nil, err
}
case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL,
opcode.JMPEQL, opcode.JMPNEL,
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLEL, opcode.JMPLTL,
opcode.CALLL, opcode.PUSHA, opcode.ENDTRYL:
// we can't use arg returned by ctx.Next() because it is copied
nextIP := ctx.NextIP()
arg := b[nextIP-4:]
offset, err := c.replaceLabelWithOffset(ctx.IP(), arg)
offset, err := c.replaceLabelWithOffset(ctx.IP(), param)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/smartcontract/convertor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 17,7 @@ func ParameterFromStackItem(i stackitem.Item, seen map[stackitem.Item]bool) Para
Type: IntegerType,
Value: i.Value().(*big.Int).Int64(),
}
case *stackitem.Bool:
case stackitem.Bool:
return Parameter{
Type: BoolType,
Value: i.Value().(bool),
Expand Down
9 changes: 4 additions & 5 deletions pkg/vm/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 97,9 @@ func (c *Context) NextIP() int {
return c.nextip
}

// Next returns the next instruction to execute with its parameter if any. After
// its invocation the instruction pointer points to the instruction being
// returned.
// Next returns the next instruction to execute with its parameter if any.
// The parameter is not copied and shouldn't be written to. After its invocation
// the instruction pointer points to the instruction being returned.
func (c *Context) Next() (opcode.Opcode, []byte, error) {
var err error

Expand Down Expand Up @@ -171,8 171,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
if err != nil {
return instr, nil, err
}
parameter := make([]byte, numtoread)
copy(parameter, c.prog[c.nextip:c.nextip numtoread])
parameter := c.prog[c.nextip : c.nextip numtoread]
c.nextip = numtoread
return instr, parameter, nil
}
Expand Down
24 changes: 23 additions & 1 deletion pkg/vm/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 195,7 @@ func compareItems(t *testing.T, a, b stackitem.Item) {
require.Equal(t, val, ac.Value().(*big.Int).Int64())
case *stackitem.ByteArray:
require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64())
case *stackitem.Bool:
case stackitem.Bool:
if ac.Value().(bool) {
require.Equal(t, val, int64(1))
} else {
Expand All @@ -208,6 208,28 @@ func compareItems(t *testing.T, a, b stackitem.Item) {
p, ok := b.(*stackitem.Pointer)
require.True(t, ok)
require.Equal(t, si.Position(), p.Position()) // there no script in test files
case *stackitem.Array, *stackitem.Struct:
require.Equal(t, a.Type(), b.Type())

as := a.Value().([]stackitem.Item)
bs := a.Value().([]stackitem.Item)
require.Equal(t, len(as), len(bs))

for i := range as {
compareItems(t, as[i], bs[i])
}

case *stackitem.Map:
require.Equal(t, a.Type(), b.Type())

as := a.Value().([]stackitem.MapElement)
bs := a.Value().([]stackitem.MapElement)
require.Equal(t, len(as), len(bs))

for i := range as {
compareItems(t, as[i].Key, bs[i].Key)
compareItems(t, as[i].Value, bs[i].Value)
}
default:
require.Equal(t, a, b)
}
Expand Down
75 changes: 35 additions & 40 deletions pkg/vm/ref_counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 5,40 @@ import (
)

// refCounter represents reference counter for the VM.
type refCounter struct {
items map[stackitem.Item]int
size int
}
type refCounter int

func newRefCounter() *refCounter {
return &refCounter{
items: make(map[stackitem.Item]int),
type (
rcInc interface {
IncRC() int
}
rcDec interface {
DecRC() int
}
)

func newRefCounter() *refCounter {
return new(refCounter)
}

// Add adds an item to the reference counter.
func (r *refCounter) Add(item stackitem.Item) {
if r == nil {
return
}
r.size
*r

switch item.(type) {
case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
if r.items[item] ; r.items[item] > 1 {
return
irc, ok := item.(rcInc)
if !ok || irc.IncRC() > 1 {
return
}
switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Add(it)
}

switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Add(it)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Add(t.Value().([]stackitem.MapElement)[i].Value)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Add(t.Value().([]stackitem.MapElement)[i].Value)
}
}
}
Expand All @@ -47,26 48,20 @@ func (r *refCounter) Remove(item stackitem.Item) {
if r == nil {
return
}
r.size--
*r--

switch item.(type) {
case *stackitem.Array, *stackitem.Struct, *stackitem.Map:
if r.items[item] > 1 {
r.items[item]--
return
irc, ok := item.(rcDec)
if !ok || irc.DecRC() > 0 {
return
}
switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Remove(it)
}

delete(r.items, item)

switch t := item.(type) {
case *stackitem.Array, *stackitem.Struct:
for _, it := range item.Value().([]stackitem.Item) {
r.Remove(it)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Remove(t.Value().([]stackitem.MapElement)[i].Value)
}
case *stackitem.Map:
for i := range t.Value().([]stackitem.MapElement) {
r.Remove(t.Value().([]stackitem.MapElement)[i].Value)
}
}
}
24 changes: 17 additions & 7 deletions pkg/vm/ref_counter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 10,34 @@ import (
func TestRefCounter_Add(t *testing.T) {
r := newRefCounter()

require.Equal(t, 0, r.size)
require.Equal(t, 0, int(*r))

r.Add(stackitem.Null{})
require.Equal(t, 1, r.size)
require.Equal(t, 1, int(*r))

r.Add(stackitem.Null{})
require.Equal(t, 2, r.size) // count scalar items twice
require.Equal(t, 2, int(*r)) // count scalar items twice

arr := stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1}), stackitem.NewBool(false)})
r.Add(arr)
require.Equal(t, 5, r.size) // array 2 elements
require.Equal(t, 5, int(*r)) // array 2 elements

r.Add(arr)
require.Equal(t, 6, r.size) // count only array
require.Equal(t, 6, int(*r)) // count only array

r.Remove(arr)
require.Equal(t, 5, r.size)
require.Equal(t, 5, int(*r))

r.Remove(arr)
require.Equal(t, 2, r.size)
require.Equal(t, 2, int(*r))
}

func BenchmarkRefCounter_Add(b *testing.B) {
a := stackitem.NewArray(nil)
rc := newRefCounter()

b.ResetTimer()
for i := 0; i < b.N; i {
rc.Add(a)
}
}
Loading

0 comments on commit 5b12dd2

Please sign in to comment.