Skip to content

Switch to Wasmtime #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
# trealla-go [![GoDoc](https://godoc.org/github.com/trealla-prolog/go?status.svg)](https://godoc.org/github.com/trealla-prolog/go)
`import "github.com/trealla-prolog/go/trealla"`

Prolog interface for Go using [Trealla Prolog](https://github.com/trealla-prolog/trealla) and [Wasmer](https://github.com/wasmerio/wasmer-go).
Prolog interface for Go using [Trealla Prolog](https://github.com/trealla-prolog/trealla) and [Wasmtime](https://github.com/bytecodealliance/wasmtime-go).
It's pretty fast. Not as fast as native Trealla, but pretty dang fast (about 2x slower than native).

**Development Status**: beta 🤠
**Development Status**: inching closer to stability

### Caveats

- Beta status, API will probably change.
- Doesn't work on Windows ([wasmer-go issue](https://github.com/wasmerio/wasmer-go/issues/69)).
- Works great on WSL.
- Beta status, <s>API will probably change</s>
- API is relatively stable now.

## Install

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module github.com/trealla-prolog/go

go 1.19

require github.com/wasmerio/wasmer-go v1.0.4
require github.com/bytecodealliance/wasmtime-go/v8 v8.0.0
16 changes: 5 additions & 11 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/bytecodealliance/wasmtime-go/v8 v8.0.0 h1:jP4sqm2PHgm3+eQ50zCoCdIyQFkIL/Rtkw6TT8OYPFI=
github.com/bytecodealliance/wasmtime-go/v8 v8.0.0/go.mod h1:tgazNLU7xSC2gfRAM8L4WyE+dgs5yp9FF5/tGebEQyM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/wasmerio/wasmer-go v1.0.4 h1:MnqHoOGfiQ8MMq2RF6wyCeebKOe84G88h5yv+vmxJgs=
github.com/wasmerio/wasmer-go v1.0.4/go.mod h1:0gzVdSfg6pysA6QVp6iVRPTagC6Wq9pOE8J86WKb2Fk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2 changes: 1 addition & 1 deletion src/trealla
7 changes: 6 additions & 1 deletion trealla/answer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type response struct {
}

func (pl *prolog) parse(goal, stdout, stderr string) (Answer, error) {
// log.Println("parse:", goal, "stdout:", stdout, "stderr:", stderr)
if len(strings.TrimSpace(stdout)) == 0 {
return Answer{}, ErrFailure{Query: goal, Stderr: stderr}
}
Expand All @@ -41,6 +42,8 @@ func (pl *prolog) parse(goal, stdout, stderr string) (Answer, error) {
butt = nl
}

// fmt.Println("OUTPUT:", stdout)

output := stdout[start+1 : end]
js := stdout[end+1 : butt]

Expand All @@ -65,9 +68,11 @@ func (pl *prolog) parse(goal, stdout, stderr string) (Answer, error) {
dec := json.NewDecoder(strings.NewReader(js))
dec.UseNumber()
if err := dec.Decode(&resp); err != nil {
return resp.Answer, fmt.Errorf("trealla: decoding error: %w", err)
return resp.Answer, fmt.Errorf("trealla: decoding error: %w (resp = %s)", err, string(js))
}

// spew.Dump(resp)

switch resp.Status {
case statusSuccess:
return resp.Answer, nil
Expand Down
4 changes: 2 additions & 2 deletions trealla/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func BenchmarkQuery(b *testing.B) {
}
}

func BenchmarkOverhead(b *testing.B) {
func BenchmarkNewProlog(b *testing.B) {
for i := 0; i < b.N; i++ {
pl, err := New()
if err != nil {
Expand Down Expand Up @@ -53,7 +53,7 @@ func BenchmarkRedo(b *testing.B) {
}

func BenchmarkTak(b *testing.B) {
pl, err := New(WithPreopenDir("testdata"))
pl, err := New(WithPreopenDir("."))
if err != nil {
b.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions trealla/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func (err ErrThrow) Error() string {
return fmt.Sprintf("trealla: exception thrown: %v", err.Ball)
}

func errUnexported(symbol string, err error) error {
return fmt.Errorf("trealla: failed to get exported function: %s: error: %w", symbol, err)
func errUnexported(symbol string) error {
return fmt.Errorf("trealla: failed to get wasm exported function: %q (symbol not found)", symbol)
}

var (
Expand Down
78 changes: 34 additions & 44 deletions trealla/interop.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package trealla

import (
"encoding/binary"
"fmt"

"github.com/wasmerio/wasmer-go/wasmer"
"github.com/bytecodealliance/wasmtime-go/v8"
)

// Predicate is a Prolog predicate implemented in Go.
Expand All @@ -22,50 +23,25 @@ type Predicate func(pl Prolog, subquery Subquery, goal Term) Term
// It is unique as long as the query is alive, but may be re-used later on.
type Subquery int32

func (pl *prolog) exports() map[string]wasmer.IntoExtern {
return map[string]wasmer.IntoExtern{
"host-call": pl.host_call(),
"host-resume": pl.host_resume(),
}
}

func (pl *prolog) host_call() *wasmer.Function {
return wasmer.NewFunctionWithEnvironment(wasmStore,
// extern int32_t host_call(int32_t subquery, const char *msg, size_t msg_size, char **reply, size_t *reply_size);
wasmer.NewFunctionType(
wasmer.NewValueTypes(wasmer.I32, wasmer.I32, wasmer.I32, wasmer.I32, wasmer.I32),
wasmer.NewValueTypes(wasmer.I32),
), pl, hostCall)
}
func (pl *prolog) hostCall( /*c *wasmtime.Caller,*/ subquery, msgptr, msgsize, reply_pp, replysize_p int32) (int32, *wasmtime.Trap) {
// extern int32_t host_call(int32_t subquery, const char *msg, size_t msg_size, char **reply, size_t *reply_size);

func (pl *prolog) host_resume() *wasmer.Function {
return wasmer.NewFunctionWithEnvironment(wasmStore,
// extern bool host_resume(int32_t subquery, char **reply, size_t *reply_size);
wasmer.NewFunctionType(
wasmer.NewValueTypes(wasmer.I32, wasmer.I32, wasmer.I32),
wasmer.NewValueTypes(wasmer.I32),
), pl, hostResume)
}

func hostCall(env any, args []wasmer.Value) ([]wasmer.Value, error) {
pl := env.(*prolog)
subquery := args[0].I32()
msgptr := args[1].I32()
msgsize := args[2].I32()
reply_pp := args[3].I32()
replysize_p := args[4].I32()
subq := pl.subquery(subquery)
if subq == nil {
return 0, wasmtime.NewTrap(fmt.Sprintf("could not find subquery: %d", subquery))
}

msgraw, err := pl.gets(msgptr, msgsize)
if err != nil {
return nil, err
return 0, wasmtime.NewTrap(err.Error())
}

msg, err := unmarshalTerm([]byte(msgraw))
if err != nil {
return nil, err
return 0, wasmtime.NewTrap(err.Error())
}

memory := pl.memory.Data()
memory := pl.memory.UnsafeData(pl.store)
reply := func(str string) error {
msg, err := newCString(pl, str)
if err != nil {
Expand All @@ -80,9 +56,9 @@ func hostCall(env any, args []wasmer.Value) ([]wasmer.Value, error) {
if !ok {
expr := typeError("atomic", msg, piTerm("$host_call", 2))
if err := reply(expr.String()); err != nil {
return nil, err
return 0, wasmtime.NewTrap(err.Error())
}
return []wasmer.Value{wasmTrue}, nil
return wasmTrue, nil
}

proc, ok := pl.procs[goal.Indicator()]
Expand All @@ -93,24 +69,38 @@ func hostCall(env any, args []wasmer.Value) ([]wasmer.Value, error) {
piTerm("$host_call", 2),
))
if err := reply(expr.String()); err != nil {
return nil, err
return 0, wasmtime.NewTrap(err.Error())
}
return []wasmer.Value{wasmTrue}, nil
return wasmTrue, nil
}

if err := subq.readOutput(); err != nil {
return 0, wasmtime.NewTrap(err.Error())
}
// log.Println("SAVING", subq.stderr.String())

locked := &lockedProlog{prolog: pl}
continuation := proc(locked, Subquery(subquery), goal)
locked.kill()
expr, err := marshal(continuation)
if err != nil {
return nil, err
return 0, wasmtime.NewTrap(err.Error())
}
if err := reply(expr); err != nil {
return nil, err
return 0, wasmtime.NewTrap(err.Error())
}

if err := subq.readOutput(); err != nil {
return 0, wasmtime.NewTrap(err.Error())
}
if _, err := pl.pl_capture.Call(pl.store, pl.ptr); err != nil {
return 0, wasmtime.NewTrap(err.Error())
}
return []wasmer.Value{wasmTrue}, nil

return wasmTrue, nil
}

func hostResume(_ any, args []wasmer.Value) ([]wasmer.Value, error) {
return []wasmer.Value{wasmFalse}, nil
func hostResume(_, _, _ int32) (int32, *wasmtime.Trap) {
// extern int32_t host_resume(int32_t subquery, char **reply, size_t *reply_size);
return wasmFalse, nil
}
18 changes: 13 additions & 5 deletions trealla/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
)

func TestInterop(t *testing.T) {
pl, err := New(WithDebugLog(log.Default()) /*WithStderrLog(log.Default()), WithStdoutLog(log.Default()), WithTrace() */)
pl, err := New(WithDebugLog(log.Default()))

if err != nil {
t.Fatal(err)
}
Expand All @@ -21,11 +22,17 @@ func TestInterop(t *testing.T) {
t.Error("bad goal. want:", want, "got:", goal)
}

ans, err := pl.QueryOnce(ctx, "X is 1 + 1.")
ans1, err := pl.QueryOnce(ctx, "X is 1 + 1.")
if err != nil {
t.Error(err)
}

ans2, err := pl.QueryOnce(ctx, "Y is X + 1.", WithBind("X", ans1.Solution["X"]))
if err != nil {
t.Error(err)
}
return Atom("interop_test").Of(ans.Solution["X"])

return Atom("interop_test").Of(ans2.Solution["Y"])
})

tests := []struct {
Expand Down Expand Up @@ -57,7 +64,7 @@ func TestInterop(t *testing.T) {
want: []Answer{
{
Query: `interop_test(X).`,
Solution: Substitution{"X": int64(2)},
Solution: Substitution{"X": int64(3)},
},
},
},
Expand All @@ -66,7 +73,7 @@ func TestInterop(t *testing.T) {
// want: []Answer{
// {
// Query: `http_fetch("https://jsonplaceholder.typicode.com/todos/1", Result, [as(json)]).`,
// Solution: Substitution{"Result": Compound{Functor: "{}", Args: []Term{Compound{Functor: ",", Args: []Term{Compound{Functor: ":", Args: []Term{"userId", 1}}, Compound{Functor: ",", Args: []Term{Compound{Functor: ":", Args: []Term{"id", 1}}, Compound{Functor: ",", Args: []Term{Compound{Functor: ":", Args: []Term{"title", "delectus aut autem"}}, Compound{Functor: ":", Args: []Term{"completed", "false"}}}}}}}}}}},
// Solution: Substitution{"Result": Compound{Functor: "{}", Args: []Term{Compound{Functor: ",", Args: []Term{Compound{Functor: ":", Args: []Term{"userId", int64(1)}}, Compound{Functor: ",", Args: []Term{Compound{Functor: ":", Args: []Term{"id", int64(1)}}, Compound{Functor: ",", Args: []Term{Compound{Functor: ":", Args: []Term{"title", "delectus aut autem"}}, Compound{Functor: ":", Args: []Term{"completed", "false"}}}}}}}}}}},
// },
// },
// },
Expand All @@ -80,6 +87,7 @@ func TestInterop(t *testing.T) {
for q.Next(ctx) {
ans = append(ans, q.Current())
}
q.Close()
err := q.Err()
if tc.err == nil && err != nil {
t.Fatal(err)
Expand Down
Binary file modified trealla/libtpl.wasm
Binary file not shown.
Loading