Merge pull request #1939 from nspcc-dev/vm-arith-null

Allow NULL items for LT/LE/GT/GE instructions
This commit is contained in:
Roman Khimov 2021-04-29 17:55:13 +03:00 committed by GitHub
commit 5924123927
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 75 additions and 61 deletions

View file

@ -1901,11 +1901,11 @@ func convertToken(tok token.Token, typ types.Type) (opcode.Opcode, error) {
case token.LSS:
return opcode.LT, nil
case token.LEQ:
return opcode.LTE, nil
return opcode.LE, nil
case token.GTR:
return opcode.GT, nil
case token.GEQ:
return opcode.GTE, nil
return opcode.GE, nil
case token.EQL:
// VM has separate opcodes for number and string equality
if isNumber(typ) {

View file

@ -173,9 +173,9 @@ var coefficients = map[opcode.Opcode]int64{
opcode.NUMEQUAL: 1 << 3,
opcode.NUMNOTEQUAL: 1 << 3,
opcode.LT: 1 << 3,
opcode.LTE: 1 << 3,
opcode.LE: 1 << 3,
opcode.GT: 1 << 3,
opcode.GTE: 1 << 3,
opcode.GE: 1 << 3,
opcode.MIN: 1 << 3,
opcode.MAX: 1 << 3,
opcode.WITHIN: 1 << 3,

View file

@ -188,9 +188,9 @@ const (
NUMEQUAL Opcode = 0xB3
NUMNOTEQUAL Opcode = 0xB4
LT Opcode = 0xB5
LTE Opcode = 0xB6
LE Opcode = 0xB6
GT Opcode = 0xB7
GTE Opcode = 0xB8
GE Opcode = 0xB8
MIN Opcode = 0xB9
MAX Opcode = 0xBA
WITHIN Opcode = 0xBB

View file

@ -170,9 +170,9 @@ func _() {
_ = x[NUMEQUAL-179]
_ = x[NUMNOTEQUAL-180]
_ = x[LT-181]
_ = x[LTE-182]
_ = x[LE-182]
_ = x[GT-183]
_ = x[GTE-184]
_ = x[GE-184]
_ = x[MIN-185]
_ = x[MAX-186]
_ = x[WITHIN-187]
@ -200,7 +200,7 @@ func _() {
_ = x[CONVERT-219]
}
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODPOWSQRTSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSPOPITEMISNULLISTYPECONVERT"
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODPOWSQRTSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLEGTGEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSPOPITEMISNULLISTYPECONVERT"
var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8],
@ -363,34 +363,34 @@ var _Opcode_map = map[Opcode]string{
179: _Opcode_name[896:904],
180: _Opcode_name[904:915],
181: _Opcode_name[915:917],
182: _Opcode_name[917:920],
183: _Opcode_name[920:922],
184: _Opcode_name[922:925],
185: _Opcode_name[925:928],
186: _Opcode_name[928:931],
187: _Opcode_name[931:937],
192: _Opcode_name[937:941],
193: _Opcode_name[941:947],
194: _Opcode_name[947:956],
195: _Opcode_name[956:964],
196: _Opcode_name[964:974],
197: _Opcode_name[974:984],
198: _Opcode_name[984:993],
200: _Opcode_name[993:999],
202: _Opcode_name[999:1003],
203: _Opcode_name[1003:1009],
204: _Opcode_name[1009:1013],
205: _Opcode_name[1013:1019],
206: _Opcode_name[1019:1027],
207: _Opcode_name[1027:1033],
208: _Opcode_name[1033:1040],
209: _Opcode_name[1040:1052],
210: _Opcode_name[1052:1058],
211: _Opcode_name[1058:1068],
212: _Opcode_name[1068:1075],
216: _Opcode_name[1075:1081],
217: _Opcode_name[1081:1087],
219: _Opcode_name[1087:1094],
182: _Opcode_name[917:919],
183: _Opcode_name[919:921],
184: _Opcode_name[921:923],
185: _Opcode_name[923:926],
186: _Opcode_name[926:929],
187: _Opcode_name[929:935],
192: _Opcode_name[935:939],
193: _Opcode_name[939:945],
194: _Opcode_name[945:954],
195: _Opcode_name[954:962],
196: _Opcode_name[962:972],
197: _Opcode_name[972:982],
198: _Opcode_name[982:991],
200: _Opcode_name[991:997],
202: _Opcode_name[997:1001],
203: _Opcode_name[1001:1007],
204: _Opcode_name[1007:1011],
205: _Opcode_name[1011:1017],
206: _Opcode_name[1017:1025],
207: _Opcode_name[1025:1031],
208: _Opcode_name[1031:1038],
209: _Opcode_name[1038:1050],
210: _Opcode_name[1050:1056],
211: _Opcode_name[1056:1066],
212: _Opcode_name[1066:1073],
216: _Opcode_name[1073:1079],
217: _Opcode_name[1079:1085],
219: _Opcode_name[1085:1092],
}
func (i Opcode) String() string {

View file

@ -571,7 +571,7 @@ func BenchmarkOpcodes(t *testing.B) {
}
binaries := []opcode.Opcode{opcode.AND, opcode.OR, opcode.XOR, opcode.ADD, opcode.SUB,
opcode.BOOLAND, opcode.BOOLOR, opcode.NUMEQUAL, opcode.NUMNOTEQUAL,
opcode.LT, opcode.LTE, opcode.GT, opcode.GTE, opcode.MIN, opcode.MAX}
opcode.LT, opcode.LE, opcode.GT, opcode.GE, opcode.MIN, opcode.MAX}
for _, op := range binaries {
t.Run(op.String(), func(t *testing.B) {
t.Run("0+0", func(t *testing.B) { benchOpcode(t, opParamPushVM(op, nil, 0, 0)) })

@ -1 +1 @@
Subproject commit 3fb22406ba72a86fb251c763aea72677f9f9baa2
Subproject commit 72e546b176401b85a03bc4653eeb177badb42d3b

View file

@ -972,25 +972,27 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) != 0)
case opcode.LT:
b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) == -1)
case opcode.LT, opcode.LE, opcode.GT, opcode.GE:
eb := v.estack.Pop()
ea := v.estack.Pop()
_, aNil := ea.Item().(stackitem.Null)
_, bNil := eb.Item().(stackitem.Null)
case opcode.LTE:
b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) <= 0)
case opcode.GT:
b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) == 1)
case opcode.GTE:
b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) >= 0)
res := !aNil && !bNil
if res {
cmp := ea.BigInt().Cmp(eb.BigInt())
switch op {
case opcode.LT:
res = cmp == -1
case opcode.LE:
res = cmp <= 0
case opcode.GT:
res = cmp == 1
case opcode.GE:
res = cmp >= 0
}
}
v.estack.PushVal(res)
case opcode.MIN:
b := v.estack.Pop().BigInt()

View file

@ -807,13 +807,25 @@ func TestSHL(t *testing.T) {
t.Run("BigResult", getTestFuncForVM(prog, nil, getBigInt(stackitem.MaxBigIntegerSizeBits/2, 0), stackitem.MaxBigIntegerSizeBits/2))
}
func TestArithNullArg(t *testing.T) {
for _, op := range []opcode.Opcode{opcode.LT, opcode.LE, opcode.GT, opcode.GE} {
prog := makeProgram(op)
t.Run(op.String(), func(t *testing.T) {
runWithArgs(t, prog, false, stackitem.Null{}, 0)
runWithArgs(t, prog, false, 0, stackitem.Null{})
runWithArgs(t, prog, nil, stackitem.NewInterop(nil), 1) // also has `.Value() == nil`
})
}
}
func TestLT(t *testing.T) {
prog := makeProgram(opcode.LT)
runWithArgs(t, prog, false, 4, 3)
}
func TestLTE(t *testing.T) {
prog := makeProgram(opcode.LTE)
func TestLE(t *testing.T) {
prog := makeProgram(opcode.LE)
runWithArgs(t, prog, true, 2, 3)
}
@ -822,8 +834,8 @@ func TestGT(t *testing.T) {
runWithArgs(t, prog, true, 9, 3)
}
func TestGTE(t *testing.T) {
prog := makeProgram(opcode.GTE)
func TestGE(t *testing.T) {
prog := makeProgram(opcode.GE)
runWithArgs(t, prog, true, 3, 3)
}