diff options
| author | Christine Dodrill <me@christine.website> | 2019-06-25 22:59:06 +0000 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2019-06-25 22:59:06 +0000 |
| commit | 96a073ecddf7eee051050347a09786dc38b3a8ac (patch) | |
| tree | 44644398eaf655e5be51cdbd8bab25330bc0bd58 /cmd | |
| parent | ab2f1f58d4de28aa708cdbc1602c5ae1a941fce6 (diff) | |
| download | x-96a073ecddf7eee051050347a09786dc38b3a8ac.tar.xz x-96a073ecddf7eee051050347a09786dc38b3a8ac.zip | |
h
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/h/compile.go | 98 | ||||
| -rw-r--r-- | cmd/h/main.go | 59 | ||||
| -rw-r--r-- | cmd/h/run.go | 76 |
3 files changed, 233 insertions, 0 deletions
diff --git a/cmd/h/compile.go b/cmd/h/compile.go new file mode 100644 index 0000000..7b4d6e3 --- /dev/null +++ b/cmd/h/compile.go @@ -0,0 +1,98 @@ +package main + +import ( + "bytes" + "context" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "text/template" + "time" + + "within.website/x/h" +) + +var ( + wat2wasmLoc string + wasmTemplateObj *template.Template +) + +func init() { + loc, err := exec.LookPath("wat2wasm") + if err != nil { + panic(err) + } + + wat2wasmLoc = loc + wasmTemplateObj = template.Must(template.New("h.wast").Parse(wasmTemplate)) +} + +// CompiledProgram is a fully parsed and compiled h program. +type CompiledProgram struct { + Source string `json:"src"` + WebAssemblyText string `json:"wat"` + Binary []byte `json:"bin"` +} + +func compile(source string) (*CompiledProgram, error) { + tree, err := h.Parse(source) + if err != nil { + return nil, err + } + + result := CompiledProgram{ + Source: source, + } + + dir, err := ioutil.TempDir("", "h") + if err != nil { + return nil, err + } + defer os.RemoveAll(dir) + + fout, err := os.Create(filepath.Join(dir, "h.wast")) + if err != nil { + return nil, err + } + + buf := bytes.NewBuffer(nil) + + err = wasmTemplateObj.Execute(buf, []byte(tree.Text)) + if err != nil { + return nil, err + } + + result.WebAssemblyText = buf.String() + _, err = fout.WriteString(result.WebAssemblyText) + if err != nil { + return nil, err + } + + fname := fout.Name() + + err = fout.Close() + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, wat2wasmLoc, fname, "-o", filepath.Join(dir, "h.wasm")) + cmd.Dir = dir + + err = cmd.Run() + if err != nil { + return nil, err + } + + data, err := ioutil.ReadFile(filepath.Join(dir, "h.wasm")) + if err != nil { + return nil, err + } + + result.Binary = data + + return &result, nil +} diff --git a/cmd/h/main.go b/cmd/h/main.go new file mode 100644 index 0000000..f7e064e --- /dev/null +++ b/cmd/h/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "flag" + "fmt" + "log" + + "within.website/x/internal" +) + +var ( + program = flag.String("p", "h", "h program to compile/run") +) + +func main() { + internal.HandleStartup() + + log.Println("compiling...") + comp, err := compile(*program) + if err != nil { + panic(err) + } + + log.Println("running...") + er, err := run(*comp) + if err != nil { + panic(err) + } + + log.Println("success!") + + log.Printf("gas used:\t%d", er.GasUsed) + log.Printf("exec time:\t%s", er.ExecTime) + log.Println("output:") + fmt.Println(er.Output) +} + +const wasmTemplate = `(module + (import "h" "h" (func $h (param i32))) + (func $h_main + (local i32 i32 i32) + (local.set 0 (i32.const 10)) + (local.set 1 (i32.const 104)) + (local.set 2 (i32.const 37)) + {{ range . -}} + {{ if eq . 32 -}} + (call $h (get_local 0)) + {{ end -}} + {{ if eq . 104 -}} + (call $h (get_local 1)) + {{ end -}} + {{ if eq . 39 -}} + (call $h (get_local 2)) + {{ end -}} + {{ end -}} + (call $h (get_local 0)) + ) + (export "main" (func $h_main)) +)` diff --git a/cmd/h/run.go b/cmd/h/run.go new file mode 100644 index 0000000..d07e51f --- /dev/null +++ b/cmd/h/run.go @@ -0,0 +1,76 @@ +package main + +import ( + "errors" + "time" + + "github.com/perlin-network/life/compiler" + "github.com/perlin-network/life/exec" +) + +type Process struct { + Source CompiledProgram + Output []byte +} + +// ResolveGlobal does nothing, currently. +func (p *Process) ResolveGlobal(module, field string) int64 { return 0 } + +// ResolveFunc resolves h's ABI and importable function. +func (p *Process) ResolveFunc(module, field string) exec.FunctionImport { + switch module { + case "h": + switch field { + case "h": + return func(vm *exec.VirtualMachine) int64 { + frame := vm.GetCurrentFrame() + data := frame.Locals[0] + p.Output = append(p.Output, byte(data)) + + return 0 + } + + default: + panic("impossible state") + } + + default: + panic("impossible state") + } +} + +type ExecResult struct { + Output string + GasUsed uint64 + ExecTime time.Duration +} + +func run(cp CompiledProgram) (*ExecResult, error) { + p := &Process{ + Source: cp, + } + + var cfg exec.VMConfig + gp := &compiler.SimpleGasPolicy{GasPerInstruction: 1} + vm, err := exec.NewVirtualMachine(cp.Binary, cfg, p, gp) + if err != nil { + return nil, err + } + + mainFunc, ok := vm.GetFunctionExport("main") + if !ok { + return nil, errors.New("impossible state: no main function exposed") + } + + t0 := time.Now() + _, err = vm.Run(mainFunc) + if err != nil { + return nil, err + } + + return &ExecResult{ + Output: string(p.Output), + GasUsed: vm.Gas, + ExecTime: time.Since(t0), + }, nil +} |
