From a5f4e36e997232a16b4cb6b126bb2006a15fce3f Mon Sep 17 00:00:00 2001 From: Meng-Ying Yang Date: Mon, 8 Apr 2019 10:35:29 +0800 Subject: core: vm: sqlvm: add opNeg Add `opNeg` supports operand negative. --- core/vm/sqlvm/runtime/instructions.go | 50 ++++++++++ core/vm/sqlvm/runtime/instructions_op_test.go | 116 ++++++++++++++++++++++++ core/vm/sqlvm/runtime/instructions_tmpl_data.go | 80 ++++++++++++++++ core/vm/sqlvm/runtime/jumptable.go | 1 + core/vm/sqlvm/runtime/opcodes.go | 1 + 5 files changed, 248 insertions(+) (limited to 'core/vm') diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go index 4b03b6494..5cd622180 100644 --- a/core/vm/sqlvm/runtime/instructions.go +++ b/core/vm/sqlvm/runtime/instructions.go @@ -366,6 +366,17 @@ func metaDynBytes(dType ast.DataType) bool { func metaAllDynBytes(op *Operand) bool { return metaAll(op, metaDynBytes) } +func metaSignedNumeric(dType ast.DataType) bool { + major, _ := ast.DecomposeDataType(dType) + if major == ast.DataTypeMajorInt || + major == ast.DataTypeMajorFixed { + return true + } + return false +} + +func metaAllSignedNumeric(op *Operand) bool { return metaAll(op, metaSignedNumeric) } + func flowCheck(ctx *common.Context, v decimal.Decimal, dType ast.DataType) (err error) { if !ctx.Opt.SafeMath { return @@ -1780,3 +1791,42 @@ func (t Tuple) concat(t2 Tuple) (t3 Tuple) { } return } + +func opNeg(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 1 { + err = se.ErrorCodeDataLengthNotMatch + return + } + op := ops[0] + + if !metaAllSignedNumeric(op) { + err = se.ErrorCodeInvalidDataType + return + } + + op2 := op.clone(true) + op2.Data = make([]Tuple, len(op.Data)) + + for i := 0; i < len(op.Data); i++ { + op2.Data[i], err = op.Data[i].neg(ctx, op2.Meta) + if err != nil { + return + } + } + + registers[output] = op2 + return +} + +func (t Tuple) neg(ctx *common.Context, meta []ast.DataType) (t2 Tuple, err error) { + t2 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t2[i] = &Raw{Value: t[i].Value.Neg()} + + err = flowCheck(ctx, t2[i].Value, meta[i]) + if err != nil { + return + } + } + return +} diff --git a/core/vm/sqlvm/runtime/instructions_op_test.go b/core/vm/sqlvm/runtime/instructions_op_test.go index 6c10a2e87..c01eb3bf2 100644 --- a/core/vm/sqlvm/runtime/instructions_op_test.go +++ b/core/vm/sqlvm/runtime/instructions_op_test.go @@ -1401,6 +1401,122 @@ func (s *instructionSuite) TestOpConcat() { s.run(testcases, opConcat) } +func (s *instructionSuite) TestOpNeg() { + testcases := []opTestcase{ + { + "Neg unary", + Instruction{ + Op: NEG, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + nil, + }, + { + "Overflow Neg", + Instruction{ + Op: NEG, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorInt, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-128)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Invalid Neg", + Instruction{ + Op: NEG, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), ast.ComposeDataType(ast.DataTypeMajorBool, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abc-1")}, rawTrue}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeInvalidDataType, + }, + { + "Invalid Neg", + Instruction{ + Op: NEG, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorBool, 0), + }, + []Tuple{ + {rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abc-1")}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeDataLengthNotMatch, + }, + } + + s.run(testcases, opNeg) +} + func (s *instructionSuite) TestOpLt() { testcases := []opTestcase{ { diff --git a/core/vm/sqlvm/runtime/instructions_tmpl_data.go b/core/vm/sqlvm/runtime/instructions_tmpl_data.go index a55dab60a..e6cf97a9a 100644 --- a/core/vm/sqlvm/runtime/instructions_tmpl_data.go +++ b/core/vm/sqlvm/runtime/instructions_tmpl_data.go @@ -840,6 +840,86 @@ var testData = &tmplData{ }, }, // -- end of CONCAT + { + TestName: "OpNeg", OpFunc: "opNeg", + Cases: []*tmplTestCase{ + { + Name: "Neg unary", + Error: "nil", OpCode: "NEG", + Inputs: []*tmplOp{ + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + }, + Data: []string{`{V: 1, V: 0, V: -1}`}, + }, + }, + Output: &tmplOp{ + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + }, + Data: []string{`{V: -1, V: 0, V: 1}`}, + }, + }, + { + Name: "Overflow Neg", + Error: "errors.ErrorCodeOverflow", OpCode: "NEG", + Inputs: []*tmplOp{ + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Int", Minor: 0}, + }, + Data: []string{`{V: -128}`}, + }, + }, + Output: &tmplOp{}, + }, + { + Name: "Invalid Neg", + Error: "errors.ErrorCodeInvalidDataType", OpCode: "NEG", + Inputs: []*tmplOp{ + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + {Major: "Bool", Minor: 0}, + }, + Data: []string{`{B: "abc-1", T}`}, + }, + }, + Output: &tmplOp{}, + }, + { + Name: "Invalid Neg", + Error: "errors.ErrorCodeDataLengthNotMatch", OpCode: "NEG", + Inputs: []*tmplOp{ + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Bool", Minor: 0}, + }, + Data: []string{`{T}`}, + }, + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + }, + Data: []string{`{B: "abc-1"}`}, + }, + }, + Output: &tmplOp{}, + }, + }, + }, + // -- end of NEG { TestName: "OpLt", OpFunc: "opLt", Cases: []*tmplTestCase{ diff --git a/core/vm/sqlvm/runtime/jumptable.go b/core/vm/sqlvm/runtime/jumptable.go index f6cad57e7..ef1fc8841 100644 --- a/core/vm/sqlvm/runtime/jumptable.go +++ b/core/vm/sqlvm/runtime/jumptable.go @@ -8,6 +8,7 @@ var jumpTable = [256]OpFunction{ DIV: opDiv, MOD: opMod, CONCAT: opConcat, + NEG: opNeg, // 0x20 LT: opLt, diff --git a/core/vm/sqlvm/runtime/opcodes.go b/core/vm/sqlvm/runtime/opcodes.go index 12a4dfefb..5a0e5dd95 100644 --- a/core/vm/sqlvm/runtime/opcodes.go +++ b/core/vm/sqlvm/runtime/opcodes.go @@ -16,6 +16,7 @@ const ( DIV MOD CONCAT + NEG ) // 0x20 range - comparison ops. -- cgit v1.2.3