mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
109 lines
4.3 KiB
Go
109 lines
4.3 KiB
Go
package jit_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/mirkobrombin/Raven/pkg/jit"
|
|
"github.com/mirkobrombin/Raven/pkg/mmu"
|
|
"github.com/mirkobrombin/Raven/pkg/x64"
|
|
)
|
|
|
|
type integerDifferentialResult struct {
|
|
regs [16]uint64
|
|
flags uint64
|
|
rip uint64
|
|
}
|
|
|
|
func runIntegerInterpreter(t *testing.T, code []byte, regs [16]uint64, flags uint64) integerDifferentialResult {
|
|
t.Helper()
|
|
mem := mmu.New()
|
|
mem.MapPage(0x1000, mmu.PermRead|mmu.PermWrite|mmu.PermExecute)
|
|
for i, opcode := range append(append([]byte(nil), code...), 0xF4) {
|
|
if err := mem.Write8(0x1000+uint64(i), opcode); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
cpu := x64.New(mem)
|
|
cpu.Regs = regs
|
|
cpu.Rflags = flags
|
|
cpu.RIP = 0x1000
|
|
if err := cpu.Run(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return integerDifferentialResult{regs: cpu.Regs, flags: cpu.Rflags, rip: cpu.RIP}
|
|
}
|
|
|
|
func runIntegerJIT(t *testing.T, code []byte, regs [16]uint64, flags uint64) integerDifferentialResult {
|
|
t.Helper()
|
|
mem := mmu.New()
|
|
mem.MapPage(0x1000, mmu.PermRead|mmu.PermWrite|mmu.PermExecute)
|
|
for i, opcode := range append(append([]byte(nil), code...), 0xF4) {
|
|
if err := mem.Write8(0x1000+uint64(i), opcode); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
cache := jit.NewCache(mem)
|
|
if !cache.EnableNativeCodegen() {
|
|
t.Skip("native codegen unavailable")
|
|
}
|
|
block := cache.Compile(0x1000)
|
|
if block == nil {
|
|
t.Fatal("integer differential fixture did not compile")
|
|
}
|
|
rip := uint64(0x1000)
|
|
instret := uint64(0)
|
|
state := &jit.State{
|
|
Regs: ®s, RIP: &rip, Rflags: &flags, Mem: mem,
|
|
XMM: &[16][2]uint64{}, Instret: &instret,
|
|
}
|
|
if err := block.Run(state); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if instret != 1 || !state.Fallback {
|
|
t.Fatalf("instret=%d fallback=%v", instret, state.Fallback)
|
|
}
|
|
return integerDifferentialResult{regs: regs, flags: flags, rip: rip}
|
|
}
|
|
|
|
func TestIntegerPipelineMatchesInterpreter(t *testing.T) {
|
|
const arithmeticFlags = uint64(1<<0 | 1<<2 | 1<<4 | 1<<6 | 1<<7 | 1<<11)
|
|
tests := []struct {
|
|
name string
|
|
code []byte
|
|
regs [16]uint64
|
|
flagMask uint64
|
|
}{
|
|
{name: "MOVSX byte to word", code: []byte{0x66, 0x0F, 0xBE, 0xC1}, regs: [16]uint64{0: 0xAAAAAAAAAAAAAAAA, 1: 0x80}, flagMask: ^uint64(0)},
|
|
{name: "MOVSX word to qword", code: []byte{0x48, 0x0F, 0xBF, 0xC1}, regs: [16]uint64{0: 1, 1: 0x8000}, flagMask: ^uint64(0)},
|
|
{name: "XCHG word", code: []byte{0x66, 0x87, 0xC8}, regs: [16]uint64{0: 0xAAAAAAAA1234, 1: 0xBBBBBBBBFEDC}, flagMask: ^uint64(0)},
|
|
{name: "XCHG dword", code: []byte{0x87, 0xC8}, regs: [16]uint64{0: 0xAAAAAAAA12345678, 1: 0xBBBBBBBBFEDCBA98}, flagMask: ^uint64(0)},
|
|
{name: "NOT qword", code: []byte{0x48, 0xF7, 0xD0}, regs: [16]uint64{0: 0x8000000000000001}, flagMask: ^uint64(0)},
|
|
{name: "NEG byte", code: []byte{0xF6, 0xD8}, regs: [16]uint64{0: 0xAAAAAAAAAAAAAA80}, flagMask: arithmeticFlags},
|
|
{name: "NEG dword", code: []byte{0xF7, 0xD8}, regs: [16]uint64{0: 0xAAAAAAAA80000000}, flagMask: arithmeticFlags},
|
|
{name: "INC word", code: []byte{0x66, 0xFF, 0xC0}, regs: [16]uint64{0: 0xAAAAAAAAAAAA7FFF}, flagMask: arithmeticFlags},
|
|
{name: "DEC qword", code: []byte{0x48, 0xFF, 0xC8}, regs: [16]uint64{0: 0x8000000000000000}, flagMask: arithmeticFlags},
|
|
{name: "IMUL3 dword", code: []byte{0x6B, 0xC1, 0x80}, regs: [16]uint64{0: 0xAAAAAAAAAAAAAAAA, 1: 0x7FFFFFFF}},
|
|
{name: "SHL byte", code: []byte{0xC0, 0xE0, 0x07}, regs: [16]uint64{0: 0xAAAAAAAAAAAAAA81}, flagMask: 1<<0 | 1<<2 | 1<<6 | 1<<7},
|
|
{name: "ROR word", code: []byte{0x66, 0xC1, 0xC8, 0x0F}, regs: [16]uint64{0: 0xAAAAAAAAAAAA8001}},
|
|
{name: "DIV dword", code: []byte{0xF7, 0xF1}, regs: [16]uint64{0: 100, 1: 7}},
|
|
{name: "IDIV dword", code: []byte{0xF7, 0xF9}, regs: [16]uint64{0: 0xFFFFFF9C, 1: 7, 2: 0xFFFFFFFF}},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
const flags = uint64(0xA53)
|
|
interpreter := runIntegerInterpreter(t, test.code, test.regs, flags)
|
|
compiled := runIntegerJIT(t, test.code, test.regs, flags)
|
|
if compiled.regs != interpreter.regs {
|
|
t.Fatalf("JIT regs=%#v interpreter regs=%#v", compiled.regs, interpreter.regs)
|
|
}
|
|
if compiled.flags&test.flagMask != interpreter.flags&test.flagMask {
|
|
t.Fatalf("JIT flags=%#x interpreter flags=%#x mask=%#x",
|
|
compiled.flags, interpreter.flags, test.flagMask)
|
|
}
|
|
if compiled.rip != 0x1000+uint64(len(test.code)) {
|
|
t.Fatalf("JIT RIP=%#x", compiled.rip)
|
|
}
|
|
})
|
|
}
|
|
}
|