Skip to content

Commit 56c79cc

Browse files
committed
Add fast call opcode for special case functions
1 parent 5db8a52 commit 56c79cc

File tree

10 files changed

+99
-24
lines changed

10 files changed

+99
-24
lines changed

ast/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ type FunctionNode struct {
128128

129129
Name string
130130
Arguments []Node
131+
Fast bool
131132
}
132133

133134
type BuiltinNode struct {

bench_test.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,68 @@ func Benchmark_access(b *testing.B) {
7575
}
7676
}
7777

78+
func Benchmark_accessFast(b *testing.B) {
79+
type Price struct {
80+
Value int
81+
}
82+
env := map[string]interface{}{
83+
"price": Price{Value: 1},
84+
}
85+
86+
program, err := expr.Compile(`price.Value > 0`, expr.Env(env))
87+
if err != nil {
88+
b.Fatal(err)
89+
}
90+
91+
for n := 0; n < b.N; n++ {
92+
_, err = vm.Run(program, env)
93+
}
94+
95+
if err != nil {
96+
b.Fatal(err)
97+
}
98+
}
99+
78100
func Benchmark_call(b *testing.B) {
79101
type Env struct {
80102
Fn func(string, string, string) bool
81103
}
82104

83-
program, err := expr.Compile(`Fn("", "", "")`, expr.Env(Env{}))
105+
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(Env{}))
84106
if err != nil {
85107
b.Fatal(err)
86108
}
87109

88-
env := Env{Fn: func(s string, s2 string, s3 string) bool {
89-
return s+s2+s3 == ""
90-
}}
110+
env := Env{
111+
Fn: func(s1, s2, s3 string) bool {
112+
return s1+s2 == s3
113+
},
114+
}
115+
116+
for n := 0; n < b.N; n++ {
117+
_, err = vm.Run(program, env)
118+
}
119+
120+
if err != nil {
121+
b.Fatal(err)
122+
}
123+
}
124+
125+
func Benchmark_callFast(b *testing.B) {
126+
type Env struct {
127+
Fn func(...interface{}) interface{}
128+
}
129+
130+
program, err := expr.Compile(`Fn("a", "b", "ab"`, expr.Env(Env{}))
131+
if err != nil {
132+
b.Fatal(err)
133+
}
134+
135+
env := Env{
136+
Fn: func(s ...interface{}) interface{} {
137+
return s[0].(string)+s[1].(string) == s[2].(string)
138+
},
139+
}
91140

92141
for n := 0; n < b.N; n++ {
93142
_, err = vm.Run(program, env)

checker/checker.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,14 @@ func (v *visitor) SliceNode(node *ast.SliceNode) reflect.Type {
314314
func (v *visitor) FunctionNode(node *ast.FunctionNode) reflect.Type {
315315
if f, ok := v.types[node.Name]; ok {
316316
if fn, ok := isFuncType(f.Type); ok {
317+
318+
if !isInterface(fn) && fn.IsVariadic() && fn.NumOut() == 1 {
319+
rest := fn.In(fn.NumIn() - 1)
320+
if rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
321+
node.Fast = true
322+
}
323+
}
324+
317325
return v.checkFunc(fn, f.Method, node, node.Name, node.Arguments)
318326
}
319327
}

compiler/compiler.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,11 @@ func (c *compiler) FunctionNode(node *ast.FunctionNode) {
429429
for _, arg := range node.Arguments {
430430
c.compile(arg)
431431
}
432-
c.emit(OpCall, c.makeConstant(
432+
op := OpCall
433+
if node.Fast {
434+
op = OpCallFast
435+
}
436+
c.emit(op, c.makeConstant(
433437
Call{
434438
Name: node.Name,
435439
Size: len(node.Arguments),

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/antonmedv/expr
33
go 1.12
44

55
require (
6-
github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b
6+
github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca
77
github.com/gdamore/tcell v1.1.2
88
github.com/rivo/tview v0.0.0-20190515161233-bd836ef13b4b
99
github.com/sanity-io/litter v1.1.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
22
github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b h1:IyTcB1l64U991qSZ0ufqiJv9GVEOUBiSPwsObDm7+cc=
33
github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
4+
github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca h1:QHbltbNkVcw97h4zA/L8gA4o3dJiFvBZ0gyZHrYXHbs=
5+
github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
46
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
57
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
68
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=

vm/opcodes.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const (
3939
OpSlice
4040
OpProperty
4141
OpCall
42+
OpCallFast
4243
OpMethod
4344
OpArray
4445
OpMap
@@ -48,5 +49,5 @@ const (
4849
OpLoad
4950
OpInc
5051
OpBegin
51-
OpEnd
52+
OpEnd // This opcode must be at the end of this list.
5253
)

vm/program.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ func (program *Program) Disassemble() string {
175175
case OpCall:
176176
constant("OpCall")
177177

178+
case OpCallFast:
179+
constant("OpCallFast")
180+
178181
case OpMethod:
179182
constant("OpMethod")
180183

vm/program_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
func TestProgram_Disassemble(t *testing.T) {
11-
for op := vm.OpPush; op < vm.OpLoad; op++ {
11+
for op := vm.OpPush; op < vm.OpEnd; op++ {
1212
program := vm.Program{
1313
Constants: []interface{}{true},
1414
Bytecode: []byte{op},

vm/vm.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
6767
switch op {
6868

6969
case OpPush:
70-
vm.push(vm.constants[vm.arg()])
70+
vm.push(vm.constant())
7171

7272
case OpPop:
7373
vm.pop()
@@ -79,10 +79,10 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
7979
vm.push(a)
8080

8181
case OpFetch:
82-
vm.push(fetch(env, vm.constants[vm.arg()]))
82+
vm.push(fetch(env, vm.constant()))
8383

8484
case OpFetchMap:
85-
vm.push(env.(map[string]interface{})[vm.constants[vm.arg()].(string)])
85+
vm.push(env.(map[string]interface{})[vm.constant().(string)])
8686

8787
case OpTrue:
8888
vm.push(true)
@@ -208,7 +208,7 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
208208

209209
case OpMatchesConst:
210210
a := vm.pop()
211-
r := vm.constants[vm.arg()].(*regexp.Regexp)
211+
r := vm.constant().(*regexp.Regexp)
212212
vm.push(r.MatchString(a.(string)))
213213

214214
case OpContains:
@@ -239,31 +239,34 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
239239

240240
case OpProperty:
241241
a := vm.pop()
242-
b := vm.constants[vm.arg()]
242+
b := vm.constant()
243243
vm.push(fetch(a, b))
244244

245245
case OpCall:
246-
call := vm.constants[vm.arg()].(Call)
247-
246+
call := vm.constant().(Call)
248247
in := make([]reflect.Value, call.Size)
249248
for i := call.Size - 1; i >= 0; i-- {
250249
in[i] = reflect.ValueOf(vm.pop())
251250
}
252-
253251
out := fetchFn(env, call.Name).Call(in)
254252
vm.push(out[0].Interface())
255253

254+
case OpCallFast:
255+
call := vm.constant().(Call)
256+
in := make([]interface{}, call.Size)
257+
for i := call.Size - 1; i >= 0; i-- {
258+
in[i] = vm.pop()
259+
}
260+
fn := fetchFn(env, call.Name).Interface()
261+
vm.push(fn.(func(...interface{}) interface{})(in...))
262+
256263
case OpMethod:
257264
call := vm.constants[vm.arg()].(Call)
258-
259265
in := make([]reflect.Value, call.Size)
260266
for i := call.Size - 1; i >= 0; i-- {
261267
in[i] = reflect.ValueOf(vm.pop())
262268
}
263-
264-
obj := vm.pop()
265-
266-
out := fetchFn(obj, call.Name).Call(in)
269+
out := fetchFn(vm.pop(), call.Name).Call(in)
267270
vm.push(out[0].Interface())
268271

269272
case OpArray:
@@ -298,18 +301,18 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
298301

299302
case OpStore:
300303
scope := vm.Scope()
301-
key := vm.constants[vm.arg()].(string)
304+
key := vm.constant().(string)
302305
value := vm.pop()
303306
scope[key] = value
304307

305308
case OpLoad:
306309
scope := vm.Scope()
307-
key := vm.constants[vm.arg()].(string)
310+
key := vm.constant().(string)
308311
vm.push(scope[key])
309312

310313
case OpInc:
311314
scope := vm.Scope()
312-
key := vm.constants[vm.arg()].(string)
315+
key := vm.constant().(string)
313316
i := scope[key].(int)
314317
i++
315318
scope[key] = i
@@ -362,6 +365,10 @@ func (vm *VM) arg() uint16 {
362365
return uint16(b0) | uint16(b1)<<8
363366
}
364367

368+
func (vm *VM) constant() interface{} {
369+
return vm.constants[vm.arg()]
370+
}
371+
365372
func (vm *VM) Stack() []interface{} {
366373
return vm.stack
367374
}

0 commit comments

Comments
 (0)