main.go raw
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "runtime"
8 "unsafe"
9
10 "github.com/ebitengine/purego"
11 )
12
13 func main() {
14 if len(os.Args) < 2 {
15 fmt.Fprintf(os.Stderr, "usage: %s <path-to-mxssa.so>\n", os.Args[0])
16 os.Exit(1)
17 }
18 soPath := os.Args[1]
19 if !filepath.IsAbs(soPath) {
20 wd, _ := os.Getwd()
21 soPath = filepath.Join(wd, soPath)
22 }
23
24 lib, err := purego.Dlopen(soPath, purego.RTLD_NOW|purego.RTLD_GLOBAL)
25 if err != nil {
26 fmt.Fprintf(os.Stderr, "dlopen %s: %v\n", soPath, err)
27 os.Exit(1)
28 }
29
30 var newProgram func() int32
31 purego.RegisterLibFunc(&newProgram, lib, "moxie_ssa_new_program")
32
33 var buildPackage func(int32, uintptr, int32, uintptr, int32) int32
34 purego.RegisterLibFunc(&buildPackage, lib, "moxie_ssa_build_package")
35
36 var memberCount func(int32) int32
37 purego.RegisterLibFunc(&memberCount, lib, "moxie_ssa_pkg_member_count")
38
39 var memberName func(int32, int32, uintptr, int32) int32
40 purego.RegisterLibFunc(&memberName, lib, "moxie_ssa_pkg_member_name")
41
42 var memberKind func(int32, uintptr, int32) int32
43 purego.RegisterLibFunc(&memberKind, lib, "moxie_ssa_member_kind")
44
45 var funcBlockCount func(int32, uintptr, int32) int32
46 purego.RegisterLibFunc(&funcBlockCount, lib, "moxie_ssa_func_block_count")
47
48 var funcParamCount func(int32, uintptr, int32) int32
49 purego.RegisterLibFunc(&funcParamCount, lib, "moxie_ssa_func_param_count")
50
51 var funcLocalCount func(int32, uintptr, int32) int32
52 purego.RegisterLibFunc(&funcLocalCount, lib, "moxie_ssa_func_local_count")
53
54 var blockInstrCount func(int32, uintptr, int32, int32) int32
55 purego.RegisterLibFunc(&blockInstrCount, lib, "moxie_ssa_block_instr_count")
56
57 var instrString func(int32, uintptr, int32, int32, int32, uintptr, int32) int32
58 purego.RegisterLibFunc(&instrString, lib, "moxie_ssa_instr_string")
59
60 var funcType func(int32, uintptr, int32, uintptr, int32) int32
61 purego.RegisterLibFunc(&funcType, lib, "moxie_ssa_func_type")
62
63 var freePkg func(int32)
64 purego.RegisterLibFunc(&freePkg, lib, "moxie_ssa_free_pkg")
65
66 var freeProg func(int32)
67 purego.RegisterLibFunc(&freeProg, lib, "moxie_ssa_free_prog")
68
69 pass := 0
70 fail := 0
71 assert := func(name string, cond bool) {
72 if cond {
73 pass++
74 } else {
75 fail++
76 fmt.Fprintf(os.Stderr, "FAIL: %s\n", name)
77 }
78 }
79
80 readNameStr := func(fn func(int32, uintptr, int32, uintptr, int32) int32, h int32, name string) string {
81 nb := []byte(name)
82 buf := make([]byte, 512)
83 n := fn(h, uintptr(unsafe.Pointer(&nb[0])), int32(len(nb)),
84 uintptr(unsafe.Pointer(&buf[0])), 512)
85 return string(buf[:n])
86 }
87
88 src := []byte(`package mypkg
89
90 var x int32
91 var y string
92
93 func add(a, b int32) int32 {
94 return a + b
95 }
96
97 func greet(name string) string {
98 return "hello " | name
99 }
100
101 type Point struct {
102 X int32
103 Y int32
104 }
105
106 const maxSize = 100
107 `)
108 name := []byte("mypkg")
109
110 // Create program.
111 progH := newProgram()
112 fmt.Printf("new_program: handle = %d\n", progH)
113 assert("program created", progH >= 0)
114
115 // Build package.
116 pkgH := buildPackage(progH,
117 uintptr(unsafe.Pointer(&src[0])), int32(len(src)),
118 uintptr(unsafe.Pointer(&name[0])), int32(len(name)),
119 )
120 fmt.Printf("build_package: handle = %d\n", pkgH)
121 assert("package built", pkgH >= 0)
122
123 if pkgH < 0 {
124 fmt.Printf("%d/%d tests passed\n", pass, pass+fail)
125 os.Exit(1)
126 }
127
128 // Member count: x, y, add, greet, Point, maxSize = 6 members.
129 mc := memberCount(pkgH)
130 fmt.Printf("member_count: %d\n", mc)
131 assert(fmt.Sprintf("member count == 6, got %d", mc), mc == 6)
132
133 // Check member names.
134 expectedMembers := map[string]bool{
135 "x": false, "y": false, "add": false,
136 "greet": false, "Point": false, "maxSize": false,
137 }
138 for i := int32(0); i < mc; i++ {
139 buf := make([]byte, 256)
140 n := memberName(pkgH, i, uintptr(unsafe.Pointer(&buf[0])), 256)
141 nm := string(buf[:n])
142 if _, ok := expectedMembers[nm]; ok {
143 expectedMembers[nm] = true
144 } else {
145 fmt.Fprintf(os.Stderr, "UNEXPECTED member[%d] = %q\n", i, nm)
146 }
147 }
148 for nm, found := range expectedMembers {
149 assert(fmt.Sprintf("member %q found", nm), found)
150 }
151
152 // Check member kinds.
153 // 1=func, 2=global, 3=type, 4=const
154 checkKind := func(mname string, expectedKind int32, kindName string) {
155 nb := []byte(mname)
156 k := memberKind(pkgH, uintptr(unsafe.Pointer(&nb[0])), int32(len(nb)))
157 assert(fmt.Sprintf("%s is %s (kind=%d, got %d)", mname, kindName, expectedKind, k), k == expectedKind)
158 }
159 checkKind("add", 1, "function")
160 checkKind("greet", 1, "function")
161 checkKind("x", 2, "global")
162 checkKind("y", 2, "global")
163 checkKind("Point", 3, "type")
164 checkKind("maxSize", 4, "const")
165
166 // Check add function has blocks (body was built).
167 addName := []byte("add")
168 blocks := funcBlockCount(pkgH, uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)))
169 fmt.Printf("add block_count: %d\n", blocks)
170 assert(fmt.Sprintf("add has blocks (got %d)", blocks), blocks > 0)
171
172 // Check add function has 2 parameters (a, b).
173 params := funcParamCount(pkgH, uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)))
174 fmt.Printf("add param_count: %d\n", params)
175 assert(fmt.Sprintf("add has 2 params (got %d)", params), params == 2)
176
177 // Check add function has locals (alloca for params + return).
178 locals := funcLocalCount(pkgH, uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)))
179 fmt.Printf("add local_count: %d\n", locals)
180 assert(fmt.Sprintf("add has locals (got %d)", locals), locals > 0)
181
182 // Check entry block has instructions.
183 instrs := blockInstrCount(pkgH, uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)), 0)
184 fmt.Printf("add block[0] instr_count: %d\n", instrs)
185 assert(fmt.Sprintf("add block[0] has instrs (got %d)", instrs), instrs > 0)
186
187 // Print all instructions for debugging.
188 for bi := int32(0); bi < blocks; bi++ {
189 ic := blockInstrCount(pkgH, uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)), bi)
190 for ii := int32(0); ii < ic; ii++ {
191 buf := make([]byte, 512)
192 n := instrString(pkgH,
193 uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)),
194 bi, ii,
195 uintptr(unsafe.Pointer(&buf[0])), 512)
196 fmt.Printf(" add.block[%d].instr[%d] = %s\n", bi, ii, string(buf[:n]))
197 }
198 }
199
200 // Check greet function.
201 greetName := []byte("greet")
202 gBlocks := funcBlockCount(pkgH, uintptr(unsafe.Pointer(&greetName[0])), int32(len(greetName)))
203 fmt.Printf("greet block_count: %d\n", gBlocks)
204 assert(fmt.Sprintf("greet has blocks (got %d)", gBlocks), gBlocks > 0)
205
206 gParams := funcParamCount(pkgH, uintptr(unsafe.Pointer(&greetName[0])), int32(len(greetName)))
207 assert(fmt.Sprintf("greet has 1 param (got %d)", gParams), gParams == 1)
208
209 // Check func type string.
210 addType := readNameStr(funcType, pkgH, "add")
211 fmt.Printf("add type: %s\n", addType)
212 assert("add has type string", len(addType) > 0)
213
214 freePkg(pkgH)
215 freeProg(progH)
216
217 runtime.KeepAlive(src)
218 runtime.KeepAlive(name)
219
220 fmt.Printf("%d/%d tests passed\n", pass, pass+fail)
221 if fail > 0 {
222 os.Exit(1)
223 }
224 }
225