vm: implement REVERSE* opcodes

Use new opcodes in the compiler instead of XSWAP/ROLL.
This commit is contained in:
Evgenii Stratonikov 2020-05-06 12:12:29 +03:00
parent c8a1188ee1
commit d18199ce42
6 changed files with 161 additions and 81 deletions

View file

@ -940,13 +940,12 @@ func (c *codegen) emitReverse(num int) {
case 2: case 2:
emit.Opcode(c.prog.BinWriter, opcode.SWAP) emit.Opcode(c.prog.BinWriter, opcode.SWAP)
case 3: case 3:
emit.Int(c.prog.BinWriter, 2) emit.Opcode(c.prog.BinWriter, opcode.REVERSE3)
emit.Opcode(c.prog.BinWriter, opcode.XSWAP) case 4:
emit.Opcode(c.prog.BinWriter, opcode.REVERSE4)
default: default:
for i := 1; i < num; i++ { emit.Int(c.prog.BinWriter, int64(num))
emit.Int(c.prog.BinWriter, int64(i)) emit.Opcode(c.prog.BinWriter, opcode.REVERSEN)
emit.Opcode(c.prog.BinWriter, opcode.ROLL)
}
} }
} }

View file

@ -79,9 +79,9 @@ const (
OLDPUSH1 Opcode = 0x51 // FIXME remove #927 OLDPUSH1 Opcode = 0x51 // FIXME remove #927
ROT Opcode = 0x51 ROT Opcode = 0x51
ROLL Opcode = 0x52 ROLL Opcode = 0x52
// REVERSE3 Opcode = 0x53 REVERSE3 Opcode = 0x53
// REVERSE4 Opcode = 0x54 REVERSE4 Opcode = 0x54
// REVERSEN Opcode = 0x55 REVERSEN Opcode = 0x55
RET Opcode = 0x66 RET Opcode = 0x66
APPCALL Opcode = 0x67 APPCALL Opcode = 0x67

View file

@ -72,6 +72,9 @@ func _() {
_ = x[OLDPUSH1-81] _ = x[OLDPUSH1-81]
_ = x[ROT-81] _ = x[ROT-81]
_ = x[ROLL-82] _ = x[ROLL-82]
_ = x[REVERSE3-83]
_ = x[REVERSE4-84]
_ = x[REVERSEN-85]
_ = x[RET-102] _ = x[RET-102]
_ = x[APPCALL-103] _ = x[APPCALL-103]
_ = x[SYSCALL-104] _ = x[SYSCALL-104]
@ -141,7 +144,7 @@ func _() {
_ = x[THROWIFNOT-241] _ = x[THROWIFNOT-241]
} }
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXSWAPXTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERTTHROWTHROWIFNOT" const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXSWAPXTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERTTHROWTHROWIFNOT"
var _Opcode_map = map[Opcode]string{ var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8], 0: _Opcode_name[0:8],
@ -205,73 +208,76 @@ var _Opcode_map = map[Opcode]string{
80: _Opcode_name[338:342], 80: _Opcode_name[338:342],
81: _Opcode_name[342:350], 81: _Opcode_name[342:350],
82: _Opcode_name[350:354], 82: _Opcode_name[350:354],
102: _Opcode_name[354:357], 83: _Opcode_name[354:362],
103: _Opcode_name[357:364], 84: _Opcode_name[362:370],
104: _Opcode_name[364:371], 85: _Opcode_name[370:378],
105: _Opcode_name[371:379], 102: _Opcode_name[378:381],
106: _Opcode_name[379:394], 103: _Opcode_name[381:388],
107: _Opcode_name[394:404], 104: _Opcode_name[388:395],
108: _Opcode_name[404:416], 105: _Opcode_name[395:403],
114: _Opcode_name[416:421], 106: _Opcode_name[403:418],
115: _Opcode_name[421:426], 107: _Opcode_name[418:428],
126: _Opcode_name[426:429], 108: _Opcode_name[428:440],
127: _Opcode_name[429:435], 114: _Opcode_name[440:445],
128: _Opcode_name[435:439], 115: _Opcode_name[445:450],
129: _Opcode_name[439:444], 126: _Opcode_name[450:453],
144: _Opcode_name[444:450], 127: _Opcode_name[453:459],
145: _Opcode_name[450:453], 128: _Opcode_name[459:463],
146: _Opcode_name[453:455], 129: _Opcode_name[463:468],
147: _Opcode_name[455:458], 144: _Opcode_name[468:474],
151: _Opcode_name[458:463], 145: _Opcode_name[474:477],
152: _Opcode_name[463:471], 146: _Opcode_name[477:479],
153: _Opcode_name[471:475], 147: _Opcode_name[479:482],
154: _Opcode_name[475:478], 151: _Opcode_name[482:487],
155: _Opcode_name[478:484], 152: _Opcode_name[487:495],
156: _Opcode_name[484:487], 153: _Opcode_name[495:499],
157: _Opcode_name[487:490], 154: _Opcode_name[499:502],
158: _Opcode_name[490:493], 155: _Opcode_name[502:508],
159: _Opcode_name[493:496], 156: _Opcode_name[508:511],
160: _Opcode_name[496:499], 157: _Opcode_name[511:514],
161: _Opcode_name[499:502], 158: _Opcode_name[514:517],
162: _Opcode_name[502:505], 159: _Opcode_name[517:520],
168: _Opcode_name[505:508], 160: _Opcode_name[520:523],
169: _Opcode_name[508:511], 161: _Opcode_name[523:526],
170: _Opcode_name[511:514], 162: _Opcode_name[526:529],
171: _Opcode_name[514:521], 168: _Opcode_name[529:532],
172: _Opcode_name[521:527], 169: _Opcode_name[532:535],
177: _Opcode_name[527:529], 170: _Opcode_name[535:538],
179: _Opcode_name[529:537], 171: _Opcode_name[538:545],
180: _Opcode_name[537:548], 172: _Opcode_name[545:551],
181: _Opcode_name[548:550], 177: _Opcode_name[551:553],
182: _Opcode_name[550:553], 179: _Opcode_name[553:561],
183: _Opcode_name[553:555], 180: _Opcode_name[561:572],
184: _Opcode_name[555:558], 181: _Opcode_name[572:574],
185: _Opcode_name[558:561], 182: _Opcode_name[574:577],
186: _Opcode_name[561:564], 183: _Opcode_name[577:579],
187: _Opcode_name[564:570], 184: _Opcode_name[579:582],
192: _Opcode_name[570:574], 185: _Opcode_name[582:585],
193: _Opcode_name[574:580], 186: _Opcode_name[585:588],
194: _Opcode_name[580:589], 187: _Opcode_name[588:594],
195: _Opcode_name[589:597], 192: _Opcode_name[594:598],
196: _Opcode_name[597:606], 193: _Opcode_name[598:604],
197: _Opcode_name[606:616], 194: _Opcode_name[604:613],
198: _Opcode_name[616:625], 195: _Opcode_name[613:621],
200: _Opcode_name[625:631], 196: _Opcode_name[621:630],
202: _Opcode_name[631:635], 197: _Opcode_name[630:640],
203: _Opcode_name[635:641], 198: _Opcode_name[640:649],
204: _Opcode_name[641:645], 200: _Opcode_name[649:655],
205: _Opcode_name[645:651], 202: _Opcode_name[655:659],
206: _Opcode_name[651:659], 203: _Opcode_name[659:665],
207: _Opcode_name[659:665], 204: _Opcode_name[665:669],
208: _Opcode_name[665:672], 205: _Opcode_name[669:675],
209: _Opcode_name[672:684], 206: _Opcode_name[675:683],
210: _Opcode_name[684:690], 207: _Opcode_name[683:689],
211: _Opcode_name[690:700], 208: _Opcode_name[689:696],
216: _Opcode_name[700:706], 209: _Opcode_name[696:708],
217: _Opcode_name[706:712], 210: _Opcode_name[708:714],
219: _Opcode_name[712:719], 211: _Opcode_name[714:724],
240: _Opcode_name[719:724], 216: _Opcode_name[724:730],
241: _Opcode_name[724:734], 217: _Opcode_name[730:736],
219: _Opcode_name[736:743],
240: _Opcode_name[743:748],
241: _Opcode_name[748:758],
} }
func (i Opcode) String() string { func (i Opcode) String() string {

View file

@ -353,9 +353,31 @@ func (s *Stack) Swap(n1, n2 int) error {
if n1 == n2 { if n1 == n2 {
return nil return nil
} }
s.swap(n1, n2)
return nil
}
func (s *Stack) swap(n1, n2 int) {
a := s.Peek(n1) a := s.Peek(n1)
b := s.Peek(n2) b := s.Peek(n2)
a.value, b.value = b.value, a.value a.value, b.value = b.value, a.value
}
// ReverseTop reverses top n items of the stack.
func (s *Stack) ReverseTop(n int) error {
if n < 0 {
return errors.New("negative index")
} else if n > s.len {
return errors.New("too big index")
} else if n <= 1 {
return nil
}
for i, j := 0, n-1; i < j; {
s.swap(i, j)
i++
j--
}
return nil return nil
} }

View file

@ -732,6 +732,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic(err.Error()) panic(err.Error())
} }
case opcode.REVERSE3, opcode.REVERSE4, opcode.REVERSEN:
n := 3
switch op {
case opcode.REVERSE4:
n = 4
case opcode.REVERSEN:
n = int(v.estack.Pop().BigInt().Int64())
}
if err := v.estack.ReverseTop(n); err != nil {
panic(err.Error())
}
// Bit operations. // Bit operations.
case opcode.INVERT: case opcode.INVERT:
// inplace // inplace

View file

@ -1049,22 +1049,32 @@ func runWithArgs(t *testing.T, prog []byte, result interface{}, args ...interfac
getTestFuncForVM(prog, result, args...)(t) getTestFuncForVM(prog, result, args...)(t)
} }
func getTestFuncForVM(prog []byte, result interface{}, args ...interface{}) func(t *testing.T) { func getCustomTestFuncForVM(prog []byte, check func(t *testing.T, v *VM), args ...interface{}) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
v := load(prog) v := load(prog)
for i := range args { for i := range args {
v.estack.PushVal(args[i]) v.estack.PushVal(args[i])
} }
if result == nil { if check == nil {
checkVMFailed(t, v) checkVMFailed(t, v)
return return
} }
runVM(t, v) runVM(t, v)
require.Equal(t, 1, v.estack.Len()) check(t, v)
require.Equal(t, makeStackItem(result), v.estack.Pop().value)
} }
} }
func getTestFuncForVM(prog []byte, result interface{}, args ...interface{}) func(t *testing.T) {
var f func(t *testing.T, v *VM)
if result != nil {
f = func(t *testing.T, v *VM) {
require.Equal(t, 1, v.estack.Len())
require.Equal(t, makeStackItem(result), v.estack.Pop().value)
}
}
return getCustomTestFuncForVM(prog, f, args...)
}
func TestNOTEQUALByteArray(t *testing.T) { func TestNOTEQUALByteArray(t *testing.T) {
prog := makeProgram(opcode.NOTEQUAL) prog := makeProgram(opcode.NOTEQUAL)
t.Run("True", getTestFuncForVM(prog, true, []byte{1, 2}, []byte{0, 1, 2})) t.Run("True", getTestFuncForVM(prog, true, []byte{1, 2}, []byte{0, 1, 2}))
@ -1568,6 +1578,37 @@ func TestROLLGood(t *testing.T) {
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
} }
func getCheckEStackFunc(items ...interface{}) func(t *testing.T, v *VM) {
return func(t *testing.T, v *VM) {
require.Equal(t, len(items), v.estack.Len())
for i := 0; i < len(items); i++ {
assert.Equal(t, makeStackItem(items[i]), v.estack.Peek(i).Item())
}
}
}
func TestREVERSE3(t *testing.T) {
prog := makeProgram(opcode.REVERSE3)
t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2))
t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3), 1, 2, 3))
}
func TestREVERSE4(t *testing.T) {
prog := makeProgram(opcode.REVERSE4)
t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2, 3))
t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4), 1, 2, 3, 4))
}
func TestREVERSEN(t *testing.T) {
prog := makeProgram(opcode.REVERSEN)
t.Run("NoArgument", getTestFuncForVM(prog, nil))
t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2, 3))
t.Run("NegativeArgument", getTestFuncForVM(prog, nil, 1, 2, -1))
t.Run("Zero", getCustomTestFuncForVM(prog, getCheckEStackFunc(3, 2, 1), 1, 2, 3, 0))
t.Run("OneItem", getCustomTestFuncForVM(prog, getCheckEStackFunc(42), 42, 1))
t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4, 5), 1, 2, 3, 4, 5, 5))
}
func TestXTUCK(t *testing.T) { func TestXTUCK(t *testing.T) {
prog := makeProgram(opcode.XTUCK) prog := makeProgram(opcode.XTUCK)
t.Run("NoItem", getTestFuncForVM(prog, nil, 1)) t.Run("NoItem", getTestFuncForVM(prog, nil, 1))