diff options
author | Meng-Ying Yang <garfield@dexon.org> | 2019-05-03 11:00:36 +0800 |
---|---|---|
committer | Meng-Ying Yang <garfield@dexon.org> | 2019-05-06 17:36:32 +0800 |
commit | 28dfe769a5cab65ed3ea981a784e2598c818afc4 (patch) | |
tree | 38aaa2303ef7ec57e31f7e1c382324cca88c5956 | |
parent | c8fd7f412af66040d7e1664f292d83a10807cdc4 (diff) | |
download | dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.gz dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.bz2 dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.lz dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.xz dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.tar.zst dexon-28dfe769a5cab65ed3ea981a784e2598c818afc4.zip |
core: vm: sqlvm: OpFunction support gas model
-rw-r--r-- | core/vm/sqlvm/errors/errors.go | 2 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/gastable.go | 42 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions.go | 270 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_test.go | 24 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/jumptable.go | 143 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/runtime.go | 8 |
6 files changed, 322 insertions, 167 deletions
diff --git a/core/vm/sqlvm/errors/errors.go b/core/vm/sqlvm/errors/errors.go index c97b3e191..c448d7b43 100644 --- a/core/vm/sqlvm/errors/errors.go +++ b/core/vm/sqlvm/errors/errors.go @@ -123,6 +123,7 @@ const ( ErrorCodeMultipleEscapeByte ErrorCodePendingEscapeByte ErrorCodeNoSuchFunction + ErrorCodeOutOfGas ) var errorCodeMap = [...]string{ @@ -157,6 +158,7 @@ var errorCodeMap = [...]string{ ErrorCodeMultipleEscapeByte: "multiple escape byte", ErrorCodePendingEscapeByte: "pending escape byte", ErrorCodeNoSuchFunction: "no such function", + ErrorCodeOutOfGas: "out of gas", } func (c ErrorCode) Error() string { diff --git a/core/vm/sqlvm/runtime/gastable.go b/core/vm/sqlvm/runtime/gastable.go new file mode 100644 index 000000000..5bffd7392 --- /dev/null +++ b/core/vm/sqlvm/runtime/gastable.go @@ -0,0 +1,42 @@ +package runtime + +import ( + "github.com/dexon-foundation/dexon/core/vm/sqlvm/common" + se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" +) + +// GasFunction accepts interface and returns consumed gas. +type GasFunction func(v interface{}) (consumed uint64) + +var ( + dummyGasFunc = func(v interface{}) uint64 { return 0 } +) + +func applyGas(ctx *common.Context, fn GasFunction, v interface{}) (err error) { + consumed := fn(v) + if consumed > ctx.GasLimit { + err = se.ErrorCodeOutOfGas + return + } + ctx.GasLimit -= consumed + return +} + +// Shared gas variables +const ( + GasArithAdd = 2 + GasArithMul = 3 + GasArithCmp = 3 + GasBoolCmp = 3 + GasMemAlloc = 10 + GasMemFree = 5 // GC work + GasMemSwap = 5 + GasMemSearch = 20 + GasStorageRead = 30 + GasStorageWrite = 50 + GasBitCmp = 5 +) + +func constGasFunc(c uint64) GasFunction { + return func(v interface{}) uint64 { return c * v.(uint64) } +} diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go index 6fa72d61a..93e43709f 100644 --- a/core/vm/sqlvm/runtime/instructions.go +++ b/core/vm/sqlvm/runtime/instructions.go @@ -32,7 +32,7 @@ var ( // OpFunction type // data could be fields Fields, pattern []byte, order Orders -type OpFunction func(ctx *common.Context, ops, registers []*Operand, output uint) error +type OpFunction func(ctx *common.Context, in Instruction) error // Instruction represents single instruction with essential information // collection. @@ -41,6 +41,10 @@ type Instruction struct { Input []*Operand Output uint Position uint32 // ast tree position + + // Runtime assigned + Registers []*Operand + GasFunc GasFunction } // Raw with embedded big.Int value or byte slice which represents the real value @@ -119,19 +123,19 @@ func (op *Operand) toUint8() ([]uint8, error) { return result, nil } -func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output uint) error { - tableIdx := input[0].Data[0][0].Value.IntPart() +func opLoad(ctx *common.Context, in Instruction) error { + tableIdx := in.Input[0].Data[0][0].Value.IntPart() if tableIdx >= int64(len(ctx.Storage.Schema)) { return se.ErrorCodeIndexOutOfRange } table := ctx.Storage.Schema[tableIdx] tableRef := schema.TableRef(tableIdx) - ids, err := input[1].toUint64() + ids, err := in.Input[1].toUint64() if err != nil { return err } - fields, err := input[2].toUint8() + fields, err := in.Input[2].toUint8() if err != nil { return err } @@ -165,7 +169,7 @@ func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output } } } - registers[output] = &op + in.Registers[in.Output] = &op return nil } @@ -414,19 +418,19 @@ func flowCheck(ctx *common.Context, v decimal.Decimal, dType ast.DataType) (err return } -func opAdd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opAdd(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) || !metaAllArith(op1) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -448,7 +452,7 @@ func opAdd(ctx *common.Context, ops, registers []*Operand, output uint) (err err data[i] = raw } - registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data} return } @@ -470,19 +474,19 @@ func (r *Raw) add(r2 *Raw) (r3 *Raw) { return } -func opMul(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opMul(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) || !metaAllArith(op1) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -504,7 +508,7 @@ func opMul(ctx *common.Context, ops, registers []*Operand, output uint) (err err data[i] = raw } - registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data} return } @@ -527,19 +531,19 @@ func (r *Raw) mul(r2 *Raw) (r3 *Raw) { return } -func opSub(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opSub(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) || !metaAllArith(op1) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -561,7 +565,7 @@ func opSub(ctx *common.Context, ops, registers []*Operand, output uint) (err err data[i] = raw } - registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data} return } @@ -584,19 +588,19 @@ func (r *Raw) sub(r2 *Raw) (r3 *Raw) { return } -func opDiv(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opDiv(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) || !metaAllArith(op1) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -617,7 +621,7 @@ func opDiv(ctx *common.Context, ops, registers []*Operand, output uint) (err err data[i] = raw } - registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data} return } @@ -653,19 +657,19 @@ func (r *Raw) div(r2 *Raw) (r3 *Raw, err error) { return } -func opMod(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opMod(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) || !metaAllArith(op1) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -686,7 +690,7 @@ func opMod(ctx *common.Context, ops, registers []*Operand, output uint) (err err data[i] = raw } - registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + in.Registers[in.Output] = &Operand{Meta: op1.cloneMeta(), Data: data} return } @@ -716,19 +720,19 @@ func (r *Raw) mod(r2 *Raw) (r3 *Raw, err error) { return } -func opLt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opLt(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -750,7 +754,7 @@ func opLt(ctx *common.Context, ops, registers []*Operand, output uint) (err erro meta[i] = boolType } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } @@ -771,19 +775,19 @@ func (r *Raw) lt(r2 *Raw) (lt bool) { return } -func opGt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opGt(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -805,7 +809,7 @@ func opGt(ctx *common.Context, ops, registers []*Operand, output uint) (err erro meta[i] = boolType } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } @@ -826,19 +830,19 @@ func (r *Raw) gt(r2 *Raw) (gt bool) { return } -func opEq(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opEq(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -860,7 +864,7 @@ func opEq(ctx *common.Context, ops, registers []*Operand, output uint) (err erro meta[i] = boolType } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } @@ -872,12 +876,12 @@ func (t Tuple) eq(t2 Tuple, meta []ast.DataType) (t3 Tuple) { return } -func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opAnd(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType @@ -889,7 +893,7 @@ func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err err return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -911,7 +915,7 @@ func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err err meta[i] = boolType } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } @@ -928,12 +932,12 @@ func (r *Raw) and(r2 *Raw) (r3 *Raw) { return } -func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opOr(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType @@ -945,7 +949,7 @@ func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err erro return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } @@ -967,7 +971,7 @@ func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err erro meta[i] = boolType } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } @@ -984,12 +988,12 @@ func (r *Raw) or(r2 *Raw) (r3 *Raw) { return } -func opNot(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 1 { +func opNot(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 1 { err = se.ErrorCodeInvalidOperandNum return } - op := ops[0] + op := in.Input[0] if !metaAllBool(op) { err = se.ErrorCodeInvalidDataType @@ -1001,7 +1005,7 @@ func opNot(ctx *common.Context, ops, registers []*Operand, output uint) (err err data[i] = op.Data[i].not() } - registers[output] = &Operand{Meta: op.cloneMeta(), Data: data} + in.Registers[in.Output] = &Operand{Meta: op.cloneMeta(), Data: data} return } @@ -1018,12 +1022,12 @@ func (r *Raw) not() (r2 *Raw) { return } -func opUnion(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opUnion(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType @@ -1057,16 +1061,16 @@ func opUnion(ctx *common.Context, ops, registers []*Operand, output uint) (err e func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) }, ) - registers[output] = op3 + in.Registers[in.Output] = op3 return } -func opIntxn(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opIntxn(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op1, op2 := ops[0], ops[1] + op1, op2 := in.Input[0], in.Input[1] if !metaAllEq(op1, op2) { err = se.ErrorCodeInvalidDataType @@ -1100,20 +1104,20 @@ func opIntxn(ctx *common.Context, ops, registers []*Operand, output uint) (err e func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) }, ) - registers[output] = op3 + in.Registers[in.Output] = op3 return } -func opLike(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 && len(ops) != 3 { +func opLike(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 && len(in.Input) != 3 { err = se.ErrorCodeInvalidOperandNum return } - op, pattern := ops[0], ops[1] + op, pattern := in.Input[0], in.Input[1] var escape *Operand - if len(ops) > 2 { - escape = ops[2] + if len(in.Input) > 2 { + escape = in.Input[2] } var cReg *regexp.Regexp @@ -1197,7 +1201,7 @@ func opLike(ctx *common.Context, ops, registers []*Operand, output uint) (err er meta[i] = boolType } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } @@ -1283,49 +1287,49 @@ func (r *Raw) like(reg *regexp.Regexp) (r2 *Raw) { return } -func opZip(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) == 0 { +func opZip(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) == 0 { err = se.ErrorCodeInvalidOperandNum return } - l, err := findMaxDataLength(ops) + l, err := findMaxDataLength(in.Input) if err != nil { return } op3 := &Operand{Meta: make([]ast.DataType, 0), Data: make([]Tuple, l)} - for i := 0; i < len(ops); i++ { - op3.Meta = append(op3.Meta, ops[i].Meta...) + for i := 0; i < len(in.Input); i++ { + op3.Meta = append(op3.Meta, in.Input[i].Meta...) } for i := 0; i < l; i++ { - if ops[0].IsImmediate { - op3.Data[i] = append(Tuple{}, ops[0].Data[0]...) + if in.Input[0].IsImmediate { + op3.Data[i] = append(Tuple{}, in.Input[0].Data[0]...) } else { - op3.Data[i] = append(Tuple{}, ops[0].Data[i]...) + op3.Data[i] = append(Tuple{}, in.Input[0].Data[i]...) } - for j := 1; j < len(ops); j++ { - if ops[j].IsImmediate { - op3.Data[i] = append(op3.Data[i], ops[j].Data[0]...) + for j := 1; j < len(in.Input); j++ { + if in.Input[j].IsImmediate { + op3.Data[i] = append(op3.Data[i], in.Input[j].Data[0]...) } else { - op3.Data[i] = append(op3.Data[i], ops[j].Data[i]...) + op3.Data[i] = append(op3.Data[i], in.Input[j].Data[i]...) } } } - registers[output] = op3 + in.Registers[in.Output] = op3 return } -func opField(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opField(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op, fields := ops[0], ops[1].Data[0] + op, fields := in.Input[0], in.Input[1].Data[0] fLen := len(fields) var fieldIdx uint16 @@ -1348,17 +1352,17 @@ func opField(ctx *common.Context, ops, registers []*Operand, output uint) (err e data[i] = tuple } - registers[output] = &Operand{Meta: meta, Data: data} + in.Registers[in.Output] = &Operand{Meta: meta, Data: data} return } // in-place Op -func opPrune(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opPrune(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op, fields := ops[0], ops[1].Data[0] + op, fields := in.Input[0], in.Input[1].Data[0] fLen := len(fields) var fieldIdx uint16 @@ -1405,12 +1409,12 @@ func pruneTuple(t Tuple, prune []int) Tuple { } // in-place Op -func opCut(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opCut(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op, slice := ops[0], ops[1].Data[0] + op, slice := in.Input[0], in.Input[1].Data[0] maxL := uint16(len(op.Meta)) start, end := value2ColIdx(slice[0].Value), maxL @@ -1429,17 +1433,17 @@ func opCut(ctx *common.Context, ops, registers []*Operand, output uint) (err err op.Data[i] = append(op.Data[i][:start], op.Data[i][end:]...) } - registers[output] = op + in.Registers[in.Output] = op return } // in-place Op -func opRange(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opRange(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op, slice := ops[0], ops[1].Data[0] + op, slice := in.Input[0], in.Input[1].Data[0] offset, err := ast.DecimalToUint64(slice[0].Value) if err != nil { @@ -1465,21 +1469,21 @@ func opRange(ctx *common.Context, ops, registers []*Operand, output uint) (err e } } - registers[output] = op + in.Registers[in.Output] = op return } // in-place Op -func opSort(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opSort(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op := ops[0] + op := in.Input[0] // orders (ascending bool, field) - orders := make([]sortOption, len(ops[1].Data)) - for i, o := range ops[1].Data { + orders := make([]sortOption, len(in.Input[1].Data)) + for i, o := range in.Input[1].Data { orders[i] = sortOption{ Asc: o[0].isTrue(), Field: uint(value2ColIdx(o[1].Value)), @@ -1529,12 +1533,12 @@ func (r *Raw) cmp(r2 *Raw) (v int) { return } -func opFilter(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opFilter(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op, filters := ops[0], ops[1] + op, filters := in.Input[0], in.Input[1] op2 := &Operand{Meta: op.cloneMeta(), Data: make([]Tuple, 0)} @@ -1544,25 +1548,25 @@ func opFilter(ctx *common.Context, ops, registers []*Operand, output uint) (err } } - registers[output] = op2 + in.Registers[in.Output] = op2 return } // Type check will ensure all cast is valid -func opCast(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opCast(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeInvalidOperandNum return } - op := ops[0] - dTypes := ops[1].Meta + op := in.Input[0] + dTypes := in.Input[1].Meta if len(dTypes) != len(op.Meta) { err = se.ErrorCodeInvalidDataType return } - op2 := &Operand{Meta: ops[1].cloneMeta(), Data: make([]Tuple, len(op.Data))} + op2 := &Operand{Meta: in.Input[1].cloneMeta(), Data: make([]Tuple, len(op.Data))} for i := 0; i < len(op.Data); i++ { op2.Data[i] = append(Tuple{}, op.Data[i]...) @@ -1578,7 +1582,7 @@ func opCast(ctx *common.Context, ops, registers []*Operand, output uint) (err er } } - registers[output] = op2 + in.Registers[in.Output] = op2 return } @@ -1773,12 +1777,12 @@ func (r *Raw) shiftBytes(src []byte, l int, signed, rPadding bool) (tgr []byte) return } -func opConcat(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) != 2 { +func opConcat(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 2 { err = se.ErrorCodeDataLengthNotMatch return } - op, op2 := ops[0], ops[1] + op, op2 := in.Input[0], in.Input[1] if !metaAllEq(op, op2) { err = se.ErrorCodeInvalidDataType @@ -1797,7 +1801,7 @@ func opConcat(ctx *common.Context, ops, registers []*Operand, output uint) (err op3.Data[i] = op.Data[i].concat(op2.Data[i]) } - registers[output] = op3 + in.Registers[in.Output] = op3 return } @@ -1811,12 +1815,12 @@ 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 { +func opNeg(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) != 1 { err = se.ErrorCodeDataLengthNotMatch return } - op := ops[0] + op := in.Input[0] if !metaAllSignedNumeric(op) { err = se.ErrorCodeInvalidDataType @@ -1833,7 +1837,7 @@ func opNeg(ctx *common.Context, ops, registers []*Operand, output uint) (err err } } - registers[output] = op2 + in.Registers[in.Output] = op2 return } @@ -1850,14 +1854,14 @@ func (t Tuple) neg(ctx *common.Context, meta []ast.DataType) (t2 Tuple, err erro return } -func opFunc(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { - if len(ops) < 2 { +func opFunc(ctx *common.Context, in Instruction) (err error) { + if len(in.Input) < 2 { err = se.ErrorCodeInvalidOperandNum return } var ( - opLength, opFuncID = ops[0], ops[1] + opLength, opFuncID = in.Input[0], in.Input[1] result *Operand length uint64 ) @@ -1882,12 +1886,12 @@ func opFunc(ctx *common.Context, ops, registers []*Operand, output uint) (err er return } - result, err = fnTable[id](ctx, ops[2:], length) + result, err = fnTable[id](ctx, in.Input[2:], length) if err != nil { return } - registers[output] = result + in.Registers[in.Output] = result return } @@ -1908,12 +1912,12 @@ func uint64ToOperands(numbers []uint64) (*Operand, error) { return result, nil } -func opRepeatPK(ctx *common.Context, input []*Operand, registers []*Operand, output int) (err error) { - tableRef, err := input[0].toTableRef() +func opRepeatPK(ctx *common.Context, in Instruction) (err error) { + tableRef, err := in.Input[0].toTableRef() if err != nil { return err } IDs := ctx.Storage.RepeatPK(ctx.Contract.Address(), tableRef) - registers[output], err = uint64ToOperands(IDs) + in.Registers[in.Output], err = uint64ToOperands(IDs) return } diff --git a/core/vm/sqlvm/runtime/instructions_test.go b/core/vm/sqlvm/runtime/instructions_test.go index a00ac12dd..7dcc29449 100644 --- a/core/vm/sqlvm/runtime/instructions_test.go +++ b/core/vm/sqlvm/runtime/instructions_test.go @@ -407,7 +407,12 @@ func (s *opLoadSuite) TestOpLoad() { reg := s.newRegisters(t.tableRef, t.ids, t.fields) loadRegister(input, reg) - err := opLoad(s.ctx, input, reg, t.outputIdx) + in := Instruction{ + Input: input, + Registers: reg, + Output: t.outputIdx, + } + err := opLoad(s.ctx, in) s.Require().Equalf(t.expectedErr, err, "testcase: [%v]", t.title) s.Require().Truef(reflect.DeepEqual(t.expectedOutput, reg[t.outputIdx]), @@ -474,7 +479,12 @@ func (s opRepeatPKSuite) TestRepeatPK() { reg := s.newRegisters(t.tableRef) input := newInput([]int{1}) loadRegister(input, reg) - err := opRepeatPK(ctx, input, reg, 0) + in := Instruction{ + Input: input, + Registers: reg, + Output: 0, + } + err := opRepeatPK(ctx, in) s.Require().Equalf(t.expectedErr, err, "testcase: [%v]", t.title) result, _ := reg[0].toUint64() s.Require().Equalf(t.expectedIDs, result, "testcase: [%v]", t.title) @@ -513,17 +523,19 @@ type instructionSuite struct { func (s *instructionSuite) run(testcases []opTestcase, opfunc OpFunction) { for idx, c := range testcases { - registers := make([]*Operand, len(c.In.Input)) + c.In.Registers = make([]*Operand, len(c.In.Input)) for i, j := 0, 0; i < len(c.In.Input); i++ { if !c.In.Input[i].IsImmediate { - registers[j] = c.In.Input[i] + c.In.Registers[j] = c.In.Input[i] j++ } } + err := opfunc( &common.Context{Opt: common.Option{SafeMath: true}}, - c.In.Input, registers, c.In.Output) + c.In, + ) s.Require().Equal( c.Err, err, "idx: %v, op: %v, case: %v\nerror not equal: %v != %v", @@ -533,7 +545,7 @@ func (s *instructionSuite) run(testcases []opTestcase, opfunc OpFunction) { continue } - result := registers[0] + result := c.In.Registers[0] s.Require().True( c.Output.Equal(result), "idx: %v, op: %v, case: %v\noutput not equal.\nExpect: %v\nResult: %v\n", diff --git a/core/vm/sqlvm/runtime/jumptable.go b/core/vm/sqlvm/runtime/jumptable.go index 2930520a1..62357c20a 100644 --- a/core/vm/sqlvm/runtime/jumptable.go +++ b/core/vm/sqlvm/runtime/jumptable.go @@ -1,39 +1,128 @@ package runtime -var jumpTable = [256]OpFunction{ +type jumpUnit struct { + Func OpFunction + GasFunc GasFunction +} + +var jumpTable = [256]jumpUnit{ // 0x10 - ADD: opAdd, - MUL: opMul, - SUB: opSub, - DIV: opDiv, - MOD: opMod, - CONCAT: opConcat, - NEG: opNeg, + ADD: { + Func: opAdd, + GasFunc: constGasFunc(GasArithAdd), + }, + MUL: { + Func: opMul, + GasFunc: constGasFunc(GasArithMul), + }, + SUB: { + Func: opSub, + GasFunc: constGasFunc(GasArithAdd), + }, + DIV: { + Func: opDiv, + GasFunc: constGasFunc(GasArithMul), + }, + MOD: { + Func: opMod, + GasFunc: constGasFunc(GasArithMul), + }, + CONCAT: { + Func: opConcat, + GasFunc: constGasFunc(GasMemAlloc), + }, + NEG: { + Func: opNeg, + GasFunc: constGasFunc(GasArithAdd), + }, // 0x20 - LT: opLt, - GT: opGt, - EQ: opEq, - AND: opAnd, - OR: opOr, - NOT: opNot, - UNION: opUnion, - INTXN: opIntxn, - LIKE: opLike, + LT: { + Func: opLt, + GasFunc: constGasFunc(GasArithCmp), + }, + GT: { + Func: opGt, + GasFunc: constGasFunc(GasArithCmp), + }, + EQ: { + Func: opEq, + GasFunc: constGasFunc(GasArithCmp), + }, + AND: { + Func: opAnd, + GasFunc: constGasFunc(GasBoolCmp), + }, + OR: { + Func: opOr, + GasFunc: constGasFunc(GasBoolCmp), + }, + NOT: { + Func: opNot, + GasFunc: constGasFunc(GasBoolCmp), + }, + UNION: { + Func: opUnion, + GasFunc: constGasFunc(GasMemAlloc + GasMemSwap), + }, + INTXN: { + Func: opIntxn, + GasFunc: constGasFunc(GasMemAlloc + GasMemSwap), + }, + LIKE: { + Func: opLike, + GasFunc: constGasFunc(GasMemSearch), + }, + + // 0x30 + REPEATPK: { + Func: opRepeatPK, + GasFunc: constGasFunc(GasStorageRead), + }, // 0x40 - ZIP: opZip, - FIELD: opField, - PRUNE: opPrune, - SORT: opSort, - FILTER: opFilter, - CAST: opCast, - CUT: opCut, - RANGE: opRange, + ZIP: { + Func: opZip, + GasFunc: constGasFunc(GasMemAlloc), + }, + FIELD: { + Func: opField, + GasFunc: constGasFunc(GasMemAlloc), + }, + PRUNE: { + Func: opPrune, + GasFunc: constGasFunc(GasMemFree), + }, + SORT: { + Func: opSort, + GasFunc: constGasFunc(GasMemSwap), + }, + FILTER: { + Func: opFilter, + GasFunc: constGasFunc(GasMemAlloc), + }, + CAST: { + Func: opCast, + GasFunc: dummyGasFunc, + }, + CUT: { + Func: opCut, + GasFunc: constGasFunc(GasMemFree), + }, + RANGE: { + Func: opRange, + GasFunc: constGasFunc(GasMemFree), + }, // 0x50 - FUNC: opFunc, + FUNC: { + Func: opFunc, + GasFunc: dummyGasFunc, + }, // 0x60 - LOAD: opLoad, + LOAD: { + Func: opLoad, + GasFunc: constGasFunc(GasStorageRead), + }, } diff --git a/core/vm/sqlvm/runtime/runtime.go b/core/vm/sqlvm/runtime/runtime.go index 3b6d49a72..f138aad26 100644 --- a/core/vm/sqlvm/runtime/runtime.go +++ b/core/vm/sqlvm/runtime/runtime.go @@ -9,12 +9,18 @@ import ( // Run is runtime entrypoint. func Run(stateDB vm.StateDB, ins []Instruction, registers []*Operand) (ret []byte, err error) { for _, in := range ins { + // load register for i := 0; i < len(in.Input); i++ { if !in.Input[i].IsImmediate { in.Input[i] = registers[in.Input[i].RegisterIndex] } } - errCode := jumpTable[in.Op](&common.Context{}, in.Input, registers, in.Output) + in.Registers = registers + + // jump table and run + jUnit := jumpTable[in.Op] + in.GasFunc = jUnit.GasFunc + errCode := jUnit.Func(&common.Context{}, in) if errCode != nil { err = se.Error{ Position: in.Position, |