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