Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

clang_test.gno

3.74 Kb · 110 lines
  1package riscv
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/moul/x/vm/vmkit/v0"
  7	"gno.land/p/nt/uassert/v0"
  8)
  9
 10// A guest compiled by a real compiler, which is the only test here that was
 11// not written by the same person who wrote the emulator.
 12//
 13// The image is guests.gno's, built from tools/riscv-guests/fnv by clang. It
 14// exercises what hand-written tests do not: a real function prologue spilling
 15// ra and s0 to a stack the host set up, .bss addressed far above a read-only
 16// text segment, LLVM's constant materialization through lui/addi pairs, and
 17// MUL in an inner loop.
 18
 19// The expected hashes come from an independent FNV-1a, not from this VM.
 20func TestClangCompiledGuest(t *testing.T) {
 21	cases := []struct {
 22		in   string
 23		want string
 24	}{
 25		{"", "811c9dc5"},
 26		{"a", "e40c292c"},
 27		{"Hello", "f55c314b"},
 28		{"gno.land", "fb7ffba0"},
 29	}
 30	for _, c := range cases {
 31		m, err := NewMachine(GuestFNV(), entry)
 32		uassert.NoError(t, err)
 33		if m == nil {
 34			continue
 35		}
 36		h := vmkit.NewTestHost().WithInput([]byte(c.in))
 37		_, status := m.Step(h, vmkit.Unmetered)
 38		uassert.Equal(t, "halted", status.String(), "input "+c.in+": "+m.Trap())
 39		uassert.Equal(t, c.want, string(h.Out()), "fnv1a of "+c.in)
 40	}
 41}
 42
 43// Compiled code pauses and resumes like anything else. This is the property a
 44// realm depends on, run against output a compiler produced rather than against
 45// a loop written to be sliceable.
 46func TestClangGuestSurvivesSlicing(t *testing.T) {
 47	image := GuestFNV()
 48	h := vmkit.NewTestHost().WithInput([]byte("gno.land"))
 49
 50	sliced, err := NewMachine(image, entry)
 51	uassert.NoError(t, err)
 52	status := vmkit.Running
 53	slices := 0
 54	for i := 0; i < 500 && status == vmkit.Running; i++ {
 55		var fresh Machine
 56		uassert.NoError(t, fresh.Restore(sliced.Snapshot()))
 57		_, status = fresh.Step(h, 3)
 58		sliced = &fresh
 59		slices++
 60	}
 61	uassert.Equal(t, "halted", status.String())
 62	uassert.True(t, slices > 10, "a sliced run must actually have been sliced")
 63	uassert.Equal(t, "fb7ffba0", string(h.Out()))
 64}
 65
 66// The guest writes its globals at 0x40000, which the linker script put well
 67// above the text segment on purpose. If it had put them adjacent, W xor X
 68// would trap on the first store, so this is the test that the memory layout
 69// the toolchain was told to use is the one the host actually provides.
 70func TestClangGuestWritesAboveItsText(t *testing.T) {
 71	m, err := NewMachine(GuestFNV(), entry)
 72	uassert.NoError(t, err)
 73	h := vmkit.NewTestHost().WithInput([]byte("gno.land"))
 74	_, status := m.Step(h, vmkit.Unmetered)
 75	uassert.Equal(t, "halted", status.String())
 76
 77	// The text pages, the .bss page at 0x40000, and the stack page at the top.
 78	uassert.True(t, m.Memory().DirtyPages() >= 3,
 79		"the guest must have written outside its text segment")
 80	uassert.True(t, m.Memory().DirtyPages() <= 8,
 81		"a 196-byte program must not have dirtied the address space")
 82}
 83
 84// Image is what every caller uses to turn words into a program, so a byte
 85// order mistake here would look like a decoder bug everywhere else.
 86func TestImageIsLittleEndian(t *testing.T) {
 87	b := Image([]uint32{0x04030201, 0xFF000000})
 88	uassert.Equal(t, 8, len(b))
 89	uassert.Equal(t, uint64(0x01), uint64(b[0]))
 90	uassert.Equal(t, uint64(0x04), uint64(b[3]))
 91	uassert.Equal(t, uint64(0x00), uint64(b[4]))
 92	uassert.Equal(t, uint64(0xFF), uint64(b[7]))
 93
 94	// And it agrees with the assembler the other tests use.
 95	words := []uint32{addi(5, 0, 1), ecall()}
 96	a, i := asm(words), Image(words)
 97	uassert.Equal(t, len(a), len(i))
 98	for k := range a {
 99		uassert.Equal(t, uint64(a[k]), uint64(i[k]))
100	}
101}
102
103// GuestFNV must not hand out a slice a caller can scribble on, because the
104// next caller gets the same program.
105func TestGuestFNVIsACopy(t *testing.T) {
106	a := GuestFNV()
107	a[0] = 0xFF
108	b := GuestFNV()
109	uassert.False(t, b[0] == 0xFF, "GuestFNV must return a fresh image each call")
110}