Skip to content

Commit 5fa2a40

Browse files
committed
Improve builtin performance
1 parent 406fb35 commit 5fa2a40

File tree

6 files changed

+90
-82
lines changed

6 files changed

+90
-82
lines changed

builtin/builtin.go

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package builtin
33
import (
44
"fmt"
55
"reflect"
6-
7-
"github.com/antonmedv/expr/vm/runtime"
86
)
97

108
var (
@@ -13,16 +11,22 @@ var (
1311
)
1412

1513
type Function struct {
16-
Name string
17-
Func func(args ...interface{}) (interface{}, error)
18-
Types []reflect.Type
19-
Validate func(args []reflect.Type) (reflect.Type, error)
14+
Name string
15+
Func func(args ...interface{}) (interface{}, error)
16+
Types []reflect.Type
17+
Validate func(args []reflect.Type) (reflect.Type, error)
18+
BuiltinId int
2019
}
2120

21+
const (
22+
Len = iota + 1
23+
Abs
24+
)
25+
2226
var Builtins = []*Function{
2327
{
24-
Name: "len",
25-
Func: runtime.Len,
28+
Name: "len",
29+
BuiltinId: Len,
2630
Validate: func(args []reflect.Type) (reflect.Type, error) {
2731
if len(args) != 1 {
2832
return anyType, fmt.Errorf("invalid number of arguments for len (expected 1, got %d)", len(args))
@@ -35,61 +39,8 @@ var Builtins = []*Function{
3539
},
3640
},
3741
{
38-
Name: "abs",
39-
Func: func(args ...interface{}) (interface{}, error) {
40-
x := args[0]
41-
switch x.(type) {
42-
case float32:
43-
if x.(float32) < 0 {
44-
return -x.(float32), nil
45-
}
46-
case float64:
47-
if x.(float64) < 0 {
48-
return -x.(float64), nil
49-
}
50-
case int:
51-
if x.(int) < 0 {
52-
return -x.(int), nil
53-
}
54-
case int8:
55-
if x.(int8) < 0 {
56-
return -x.(int8), nil
57-
}
58-
case int16:
59-
if x.(int16) < 0 {
60-
return -x.(int16), nil
61-
}
62-
case int32:
63-
if x.(int32) < 0 {
64-
return -x.(int32), nil
65-
}
66-
case int64:
67-
if x.(int64) < 0 {
68-
return -x.(int64), nil
69-
}
70-
case uint:
71-
if x.(uint) < 0 {
72-
return -x.(uint), nil
73-
}
74-
case uint8:
75-
if x.(uint8) < 0 {
76-
return -x.(uint8), nil
77-
}
78-
case uint16:
79-
if x.(uint16) < 0 {
80-
return -x.(uint16), nil
81-
}
82-
case uint32:
83-
if x.(uint32) < 0 {
84-
return -x.(uint32), nil
85-
}
86-
case uint64:
87-
if x.(uint64) < 0 {
88-
return -x.(uint64), nil
89-
}
90-
}
91-
return nil, fmt.Errorf("invalid argument for abs (type %T)", x)
92-
},
42+
Name: "abs",
43+
BuiltinId: Abs,
9344
Validate: func(args []reflect.Type) (reflect.Type, error) {
9445
if len(args) != 1 {
9546
return anyType, fmt.Errorf("invalid number of arguments for abs (expected 1, got %d)", len(args))

compiler/compiler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,10 @@ func (c *compiler) CallNode(node *ast.CallNode) {
527527
c.compile(arg)
528528
}
529529
if node.Func != nil {
530+
if node.Func.BuiltinId > 0 {
531+
c.emit(OpCallBuiltin, node.Func.BuiltinId)
532+
return
533+
}
530534
switch len(node.Arguments) {
531535
case 0:
532536
c.emit(OpCall0, c.addFunction(node))

vm/opcodes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const (
66
OpPush Opcode = iota
77
OpPushInt
88
OpPop
9-
OpRot
109
OpLoadConst
1110
OpLoadField
1211
OpLoadFast
@@ -55,6 +54,7 @@ const (
5554
OpCallN
5655
OpCallFast
5756
OpCallTyped
57+
OpCallBuiltin
5858
OpArray
5959
OpMap
6060
OpLen

vm/program.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ func (program *Program) Disassemble() string {
7070
case OpPop:
7171
code("OpPop")
7272

73-
case OpRot:
74-
code("OpRot")
75-
7673
case OpLoadConst:
7774
constant("OpLoadConst")
7875

@@ -217,6 +214,9 @@ func (program *Program) Disassemble() string {
217214
case OpCallTyped:
218215
argument("OpCallTyped")
219216

217+
case OpCallBuiltin:
218+
argument("OpCallBuiltin")
219+
220220
case OpArray:
221221
code("OpArray")
222222

vm/runtime/runtime.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,13 @@ func In(needle interface{}, array interface{}) bool {
242242
panic(fmt.Sprintf(`operator "in"" not defined on %T`, array))
243243
}
244244

245-
func Len(args ...interface{}) (interface{}, error) {
246-
v := reflect.ValueOf(args[0])
245+
func Len(a interface{}) interface{} {
246+
v := reflect.ValueOf(a)
247247
switch v.Kind() {
248248
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
249-
return v.Len(), nil
249+
return v.Len()
250250
default:
251-
return 0, fmt.Errorf("invalid argument for len (type %T)", args[0])
251+
panic(fmt.Sprintf("invalid argument for len (type %T)", a))
252252
}
253253
}
254254

@@ -404,3 +404,57 @@ func IsNil(v interface{}) bool {
404404
return false
405405
}
406406
}
407+
408+
func Abs(x interface{}) interface{} {
409+
switch x.(type) {
410+
case float32:
411+
if x.(float32) < 0 {
412+
return -x.(float32)
413+
}
414+
case float64:
415+
if x.(float64) < 0 {
416+
return -x.(float64)
417+
}
418+
case int:
419+
if x.(int) < 0 {
420+
return -x.(int)
421+
}
422+
case int8:
423+
if x.(int8) < 0 {
424+
return -x.(int8)
425+
}
426+
case int16:
427+
if x.(int16) < 0 {
428+
return -x.(int16)
429+
}
430+
case int32:
431+
if x.(int32) < 0 {
432+
return -x.(int32)
433+
}
434+
case int64:
435+
if x.(int64) < 0 {
436+
return -x.(int64)
437+
}
438+
case uint:
439+
if x.(uint) < 0 {
440+
return -x.(uint)
441+
}
442+
case uint8:
443+
if x.(uint8) < 0 {
444+
return -x.(uint8)
445+
}
446+
case uint16:
447+
if x.(uint16) < 0 {
448+
return -x.(uint16)
449+
}
450+
case uint32:
451+
if x.(uint32) < 0 {
452+
return -x.(uint32)
453+
}
454+
case uint64:
455+
if x.(uint64) < 0 {
456+
return -x.(uint64)
457+
}
458+
}
459+
panic(fmt.Sprintf("invalid argument for abs (type %T)", x))
460+
}

vm/vm.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"regexp"
99
"strings"
1010

11+
"github.com/antonmedv/expr/builtin"
1112
"github.com/antonmedv/expr/file"
1213
"github.com/antonmedv/expr/vm/runtime"
1314
)
@@ -95,12 +96,6 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
9596
case OpPop:
9697
vm.pop()
9798

98-
case OpRot:
99-
b := vm.pop()
100-
a := vm.pop()
101-
vm.push(b)
102-
vm.push(a)
103-
10499
case OpLoadConst:
105100
vm.push(runtime.Fetch(env, program.Constants[arg]))
106101

@@ -372,6 +367,14 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
372367
out := vm.call(fn, arg)
373368
vm.push(out)
374369

370+
case OpCallBuiltin:
371+
switch arg {
372+
case builtin.Len:
373+
vm.push(runtime.Len(vm.pop()))
374+
case builtin.Abs:
375+
vm.push(runtime.Abs(vm.pop()))
376+
}
377+
375378
case OpArray:
376379
size := vm.pop().(int)
377380
array := make([]interface{}, size)
@@ -399,11 +402,7 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
399402
}
400403

401404
case OpLen:
402-
l, err := runtime.Len(vm.current())
403-
if err != nil {
404-
panic(err)
405-
}
406-
vm.push(l)
405+
vm.push(runtime.Len(vm.current()))
407406

408407
case OpCast:
409408
t := arg

0 commit comments

Comments
 (0)