func TestIncDec(t *testing.T) { cases := []simple{ { func(a *Assembler) { a.Mov(Rdi, Rax) a.Inc(Rax) }, []uintptr{0, 1, 10, 11}, }, { func(a *Assembler) { a.Mov(Rdi, Rax) a.Dec(Rax) }, []uintptr{1, 0, 11, 10}, }, { func(a *Assembler) { a.Mov(Imm{0x11223344}, Indirect{Rdi, 0, 32}) a.Incb(Indirect{Rdi, 1, 8}) a.Mov(Indirect{Rdi, 0, 32}, Eax) }, []uintptr{gojit.Addr(mem), 0x11223444}, }, { func(a *Assembler) { a.Mov(Imm{0x11223344}, Indirect{Rdi, 0, 32}) a.Decb(Indirect{Rdi, 1, 8}) a.Mov(Indirect{Rdi, 0, 32}, Eax) }, []uintptr{gojit.Addr(mem), 0x11223244}, }, } testSimple("inc/dec", t, cases) }
func emitRbrac(asm *amd64.Assembler, cc *compiled) { header := cc.stack[len(cc.stack)-1] cc.stack = cc.stack[:len(cc.stack)-1] asm.JmpRel(gojit.Addr(asm.Buf[header:])) end := asm.Off asm.Off = header asm.Testb(amd64.Imm{0xff}, amd64.Indirect{amd64.Rax, 0, 8}) asm.JccRel(amd64.CC_Z, gojit.Addr(asm.Buf[end:])) asm.Off = end }
func TestMov(t *testing.T) { cases := []simple{ { func(a *Assembler) { a.Mov(Imm{U32(0xdeadbeef)}, Rax) }, []uintptr{0, 0xdeadbeef}, }, { func(a *Assembler) { a.Mov(Rdi, Rax) }, []uintptr{0, 0, 1, 1, 0xdeadbeef, 0xdeadbeef, 0xffffffffffffffff, 0xffffffffffffffff}, }, { func(a *Assembler) { a.Mov(Imm{U32(0xcafebabe)}, Indirect{Rdi, 0, 64}) a.Mov(Indirect{Rdi, 0, 64}, Rax) }, []uintptr{gojit.Addr(mem), 0xffffffffcafebabe}, }, { func(a *Assembler) { a.Mov(Imm{U32(0xf00dface)}, R10) a.Mov(R10, Rax) }, []uintptr{0, 0xf00dface}, }, } testSimple("mov", t, cases) }
func (a *Assembler) rel32(addr uintptr) { off := uintptr(addr) - gojit.Addr(a.Buf[a.Off:]) - 4 if uintptr(int32(off)) != off { panic("call rel: target out of range") } a.int32(uint32(off)) }
func emitLbrac(asm *amd64.Assembler, cc *compiled) { cc.stack = append(cc.stack, asm.Off) asm.Testb(amd64.Imm{0xff}, amd64.Indirect{amd64.Rax, 0, 8}) asm.JccRel(amd64.CC_Z, gojit.Addr(asm.Buf[asm.Off:])) }
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) } } } }