func newAsm(t testing.TB) *Assembler { buf, e := gojit.Alloc(gojit.PageSize) if e != nil { t.Fatalf("alloc: ", e.Error()) } return &Assembler{buf, 0, CgoABI} }
func testSimple(name string, t *testing.T, cases []simple) { buf, e := gojit.Alloc(gojit.PageSize) if e != nil { t.Fatalf(e.Error()) } defer gojit.Release(buf) for i, tc := range cases { asm := &Assembler{Buf: buf} begin(asm) tc.f(asm) f := finish(asm) runtime.GC() for j := 0; j < len(tc.inout); j += 2 { in := tc.inout[j] out := tc.inout[j+1] got := f(in) if out != got { t.Errorf("f(%s)[%d](%x) = %x, expect %x", name, i, in, got, out) } } } }
func NewGoABI(size int) (*Assembler, error) { buf, e := gojit.Alloc(size) if e != nil { return nil, e } return &Assembler{Buf: buf, ABI: GoABI}, nil }
// Compile compiles a brainfuck program (represented as a byte slice) // into a Go function. The function accepts as an argument the tape to // operate on. The provided Reader and Writer are used to implement // `,' and `.', respectively. // // The compiled code does no bounds-checking on the tape. On EOF or // other read error, `,' clears the current cell. func Compile(prog []byte, r io.Reader, w io.Writer) (func([]byte), error) { buf, e := gojit.Alloc(gojit.PageSize * 4) if e != nil { return nil, e } cc := &compiled{buf: buf, r: r.Read, w: w.Write} asm := &amd64.Assembler{Buf: buf, ABI: abi} asm.Mov(amd64.Indirect{amd64.Rdi, 0, 64}, amd64.Rax) opcodes, e := optimize(prog) if e != nil { return nil, e } for _, op := range opcodes { switch op.op { case '+': asm.Addb(amd64.Imm{int32(op.repeat)}, amd64.Indirect{amd64.Rax, 0, 8}) case '-': asm.Subb(amd64.Imm{int32(op.repeat)}, amd64.Indirect{amd64.Rax, 0, 8}) case '<': asm.Sub(amd64.Imm{int32(op.repeat)}, amd64.Rax) case '>': asm.Add(amd64.Imm{int32(op.repeat)}, amd64.Rax) case '.': emitDot(asm, cc) case ',': emitComma(asm, cc) case '[': emitLbrac(asm, cc) case ']': emitRbrac(asm, cc) } } asm.Ret() asm.BuildTo(&cc.code) return cc.run, nil }
func TestArith(t *testing.T) { cases := []struct { insn *Instruction lhs, rhs int32 out uintptr }{ {InstAdd, 20, 30, 50}, {InstAdd, 0x7fffffff, 0x70000001, 0xf0000000}, {InstAnd, 0x77777777, U32(0xffffffff), 0x77777777}, {InstAnd, 0x77777777, U32(0x88888888), 0}, {InstOr, 0x77777777, U32(0x88888888), 0xffffffff}, {InstOr, 1, 0, 1}, {InstSub, 5, 10, 5}, {InstSub, 10, 5, 0xfffffffffffffffb}, } buf, e := gojit.Alloc(gojit.PageSize) if e != nil { t.Fatalf(e.Error()) } defer gojit.Release(buf) for _, tc := range cases { asm := &Assembler{buf, 0, CgoABI} var funcs []func(uintptr) uintptr if tc.insn.imm_r.ok() { begin(asm) asm.Mov(Imm{tc.rhs}, Rax) asm.Arithmetic(tc.insn, Imm{tc.lhs}, Rax) funcs = append(funcs, finish(asm)) } if tc.insn.imm_rm.op.ok() { begin(asm) asm.Mov(Imm{0}, Indirect{Rdi, 0, 0}) asm.Mov(Imm{tc.rhs}, Indirect{Rdi, 0, 32}) asm.Arithmetic(tc.insn, Imm{tc.lhs}, Indirect{Rdi, 0, 64}) asm.Mov(Indirect{Rdi, 0, 64}, Rax) funcs = append(funcs, finish(asm)) } if tc.insn.r_rm.ok() { begin(asm) asm.Mov(Imm{tc.lhs}, R10) asm.Mov(Imm{0}, Indirect{Rdi, 0, 0}) asm.Mov(Imm{tc.rhs}, Indirect{Rdi, 0, 32}) asm.Arithmetic(tc.insn, R10, Indirect{Rdi, 0, 64}) asm.Mov(Indirect{Rdi, 0, 64}, Rax) funcs = append(funcs, finish(asm)) } if tc.insn.rm_r.ok() { begin(asm) asm.Mov(Imm{0}, Indirect{Rdi, 0, 0}) asm.Mov(Imm{tc.lhs}, Indirect{Rdi, 0, 32}) asm.Mov(Imm{tc.rhs}, R10) asm.Arithmetic(tc.insn, Indirect{Rdi, 0, 64}, R10) asm.Mov(R10, Rax) funcs = append(funcs, finish(asm)) } for i, f := range funcs { got := f(gojit.Addr(mem)) if got != tc.out { t.Errorf("%s(0x%x,0x%x) [%d] = 0x%x (expect 0x%x)", tc.insn.Mnemonic, tc.lhs, tc.rhs, i, got, tc.out) } else if testing.Verbose() { // We don't use `testing.Logf` because // if we panic inside JIT'd code, the // runtime dies horrible (rightfully // so!), and so the `testing` cleanup // code never runs, and we never see // log messages. We want to get these // out as soon as possible, so we // write them directly. fmt.Printf("OK %d %s(0x%x,0x%x) = 0x%x\n", i, tc.insn.Mnemonic, tc.lhs, tc.rhs, got) } } } }