From 324107b31ee113a60d7739fee867e01beb30c298 Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Mon, 18 Jan 2021 16:15:04 +0300 Subject: [PATCH 1/2] vm: implement POPITEM opcode --- pkg/core/fee/opcode.go | 1 + pkg/vm/opcode/opcode.go | 1 + pkg/vm/opcode/opcode_string.go | 10 ++++++---- pkg/vm/vm.go | 14 ++++++++++++++ pkg/vm/vm_test.go | 30 ++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/pkg/core/fee/opcode.go b/pkg/core/fee/opcode.go index 822a229ad..e3e028d28 100644 --- a/pkg/core/fee/opcode.go +++ b/pkg/core/fee/opcode.go @@ -195,6 +195,7 @@ var coefficients = map[opcode.Opcode]int64{ opcode.REVERSEITEMS: 1 << 13, opcode.REMOVE: 1 << 4, opcode.CLEARITEMS: 1 << 4, + opcode.POPITEM: 1 << 4, opcode.ISNULL: 1 << 1, opcode.ISTYPE: 1 << 1, opcode.CONVERT: 1 << 11, diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index 7b01823c6..f2fcca417 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -212,6 +212,7 @@ const ( REVERSEITEMS Opcode = 0xD1 REMOVE Opcode = 0xD2 CLEARITEMS Opcode = 0xD3 + POPITEM Opcode = 0xD4 // Types ISNULL Opcode = 0xD8 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index 30260a48e..d77f8e4b6 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -192,12 +192,13 @@ func _() { _ = x[REVERSEITEMS-209] _ = x[REMOVE-210] _ = x[CLEARITEMS-211] + _ = x[POPITEM-212] _ = x[ISNULL-216] _ = x[ISTYPE-217] _ = x[CONVERT-219] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSPOPITEMISNULLISTYPECONVERT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -382,9 +383,10 @@ var _Opcode_map = map[Opcode]string{ 209: _Opcode_name[1033:1045], 210: _Opcode_name[1045:1051], 211: _Opcode_name[1051:1061], - 216: _Opcode_name[1061:1067], - 217: _Opcode_name[1067:1073], - 219: _Opcode_name[1073:1080], + 212: _Opcode_name[1061:1068], + 216: _Opcode_name[1068:1074], + 217: _Opcode_name[1074:1080], + 219: _Opcode_name[1080:1087], } func (i Opcode) String() string { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0b751e043..6e92c2298 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1216,6 +1216,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("CLEARITEMS: invalid type") } + case opcode.POPITEM: + arr := v.estack.Pop().Item() + elems := arr.Value().([]stackitem.Item) + index := len(elems) - 1 + elem := elems[index] + switch item := arr.(type) { + case *stackitem.Array: + item.Remove(index) + case *stackitem.Struct: + item.Remove(index) + } + v.refs.Remove(elem) + v.estack.PushVal(elem) + case opcode.SIZE: elem := v.estack.Pop() // Cause there is no native (byte) item type here, hence we need to check diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 4855d28ea..633fd65ce 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -2167,6 +2167,36 @@ func TestCLEARITEMS(t *testing.T) { }) } +func TestPOPITEM(t *testing.T) { + testPOPITEM := func(t *testing.T, item, elem, arr interface{}) { + prog := makeProgram(opcode.DUP, opcode.POPITEM) + v := load(prog) + v.estack.PushVal(item) + + runVM(t, v) + require.EqualValues(t, stackitem.Make(elem), v.estack.Pop().Item()) + require.EqualValues(t, stackitem.Make(arr), v.estack.Pop().Item()) + } + + elems := []stackitem.Item{stackitem.Make(11), stackitem.Make(31)} + t.Run("Array", func(t *testing.T) { + testPOPITEM(t, stackitem.NewArray(elems), 31, elems[:1]) + }) + t.Run("Struct", func(t *testing.T) { + testPOPITEM(t, stackitem.NewStruct(elems), 31, elems[:1]) + }) + t.Run("0-length array", func(t *testing.T) { + prog := makeProgram(opcode.NEWARRAY0, opcode.POPITEM) + v := load(prog) + checkVMFailed(t, v) + }) + t.Run("primitive type", func(t *testing.T) { + prog := makeProgram(opcode.PUSH4, opcode.POPITEM) + v := load(prog) + checkVMFailed(t, v) + }) +} + func TestSWAPGood(t *testing.T) { prog := makeProgram(opcode.SWAP) vm := load(prog) From f0fe03117ad2f1d3770bb566c4d2f21fbe36c91e Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Mon, 18 Jan 2021 16:19:09 +0300 Subject: [PATCH 2/2] compiler: optimize struct copy a bit POPITEM is cheaper than PUSH + PICKITEM. --- pkg/compiler/codegen.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index d62474aef..138513962 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -852,7 +852,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { // This way even non-pointer struct fields will be copied. emit.Opcodes(c.prog.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.ROT, opcode.APPEND, - opcode.PUSH0, opcode.PICKITEM) + opcode.POPITEM) } } // Do not swap for builtin functions.