Commit 38ee0891 authored by Inphi's avatar Inphi Committed by GitHub

cannon: Add more 64-bit tests (#12839)

* cannon: Add more 64-bit tests

* address review comments
parent 9ae58ba0
......@@ -18,6 +18,9 @@ endif
# The MIPS64 r1 opcodes not supported by cannon. This list does not include coprocess-specific opcodes.
UNSUPPORTED_OPCODES := (dclo|dclz)
CANNON32_FUZZTIME := 10s
CANNON64_FUZZTIME := 20s
cannon32-impl:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon32 -v $(LDFLAGS) -o ./bin/cannon32-impl .
......@@ -87,28 +90,28 @@ cannon-stf-verify:
fuzz:
printf "%s\n" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT32 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallBrk64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallMmap64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallExitGroup64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallFcntl64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintRead64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateHintWrite64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallCloneMT64 ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateHintRead ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateHintWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateConsistencyMulOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateConsistencyMultOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateConsistencyMultuOp ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateHintRead ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateHintWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \
| parallel -j 8 {}
.PHONY: \
......
......@@ -455,10 +455,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem
assertMips64(insn)
return rt
default:
panic("invalid instruction")
panic(fmt.Sprintf("invalid instruction: %x", insn))
}
}
panic("invalid instruction")
panic(fmt.Sprintf("invalid instruction: %x", insn))
}
func SignExtend(dat Word, idx Word) Word {
......
......@@ -8,22 +8,12 @@ import (
"os"
"testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
"github.com/stretchr/testify/require"
)
func TestEVMSingleStep_Operators64(t *testing.T) {
cases := []struct {
name string
isImm bool
rs Word
rt Word
imm uint16
opcode uint32
funct uint32
expectRes Word
}{
func TestEVM_SingleStep_Operators64(t *testing.T) {
cases := []operatorTestCase{
{name: "dadd. both unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // dadd t0, s1, s2
{name: "dadd. unsigned 32 and signed", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(^uint32(0)), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2
{name: "dadd. signed and unsigned 32", funct: 0x2c, isImm: false, rs: Word(^uint32(0)), rt: Word(0x12), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2
......@@ -93,52 +83,26 @@ func TestEVMSingleStep_Operators64(t *testing.T) {
{name: "dsrav max", funct: 0x17, rt: Word(0x7F_FF_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x0)},
{name: "dsrav max sign-extend", funct: 0x17, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FF)},
}
testOperators(t, cases, false)
}
v := GetMultiThreadedTestCase(t)
for i, tt := range cases {
testName := fmt.Sprintf("%v %v", v.Name, tt.name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0))
state := goVm.GetState()
var insn uint32
var rsReg uint32 = 17
var rtReg uint32
var rdReg uint32
if tt.isImm {
rtReg = 8
insn = tt.opcode<<26 | rsReg<<21 | rtReg<<16 | uint32(tt.imm)
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[rsReg] = tt.rs
} else {
rtReg = 18
rdReg = 8
insn = rsReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct
state.GetRegistersRef()[rsReg] = tt.rs
state.GetRegistersRef()[rtReg] = tt.rt
}
testutil.StoreInstruction(state.GetMemory(), 0, insn)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
if tt.isImm {
expected.Registers[rtReg] = tt.expectRes
} else {
expected.Registers[rdReg] = tt.expectRes
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
func TestEVM_SingleStep_Bitwise64(t *testing.T) {
cases := []operatorTestCase{
{name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2
{name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40
{name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2
{name: "ori", opcode: 0xd, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // ori t0, s1, 40
{name: "xor", funct: 0x26, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1370)}, // xor t0, s1, s2
{name: "xori", opcode: 0xe, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // xori t0, s1, 40
{name: "nor", funct: 0x27, isImm: false, rs: Word(0x4b0), rt: Word(0x1ea), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FA_05)}, // nor t0, s1, s2
{name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(0)}, // slt t0, s1, s2
{name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FF_FF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2
{name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2
}
testOperators(t, cases, false)
}
func TestEVMSingleStep_Shift64(t *testing.T) {
func TestEVM_SingleStep_Shift64(t *testing.T) {
cases := []struct {
name string
rd Word
......@@ -218,17 +182,10 @@ func TestEVMSingleStep_Shift64(t *testing.T) {
}
}
func TestEVMSingleStep_LoadStore64(t *testing.T) {
cases := []struct {
name string
rs Word
rt Word
opcode uint32
memVal Word
expectMemVal Word
expectRes Word
imm uint16
}{
func TestEVM_SingleStep_LoadStore64(t *testing.T) {
t1 := Word(0xFF000000_00000108)
cases := []loadStoreTestCase{
{name: "lb 0", opcode: uint32(0x20), memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x71)}, // lb $t0, 0($t1)
{name: "lb 1", opcode: uint32(0x20), imm: 1, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x72)}, // lb $t0, 1($t1)
{name: "lb 2", opcode: uint32(0x20), imm: 2, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x73)}, // lb $t0, 2($t1)
......@@ -421,63 +378,15 @@ func TestEVMSingleStep_LoadStore64(t *testing.T) {
{name: "sd", opcode: uint32(0x3f), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sd $t0, 0($t1)
{name: "sd signed", opcode: uint32(0x3f), rt: Word(0x81_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x81_22_33_44_55_66_77_88)}, // sd $t0, 4($t1)
}
v := GetMultiThreadedTestCase(t)
var t1 Word = 0xFF000000_00000108
var baseReg uint32 = 9
var rtReg uint32 = 8
for i, tt := range cases {
testName := fmt.Sprintf("%v %v", v.Name, tt.name)
t.Run(testName, func(t *testing.T) {
effAddr := arch.AddressMask & t1
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0))
state := goVm.GetState()
insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm)
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = t1
testutil.StoreInstruction(state.GetMemory(), 0, insn)
state.GetMemory().SetWord(t1&arch.AddressMask, tt.memVal)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
if tt.expectMemVal != 0 {
expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal)
} else {
expected.Registers[rtReg] = tt.expectRes
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
// use a fixed base for all tests
for i := range cases {
cases[i].base = t1
}
testLoadStore(t, cases)
}
func TestEVMSingleStep_DivMult64(t *testing.T) {
cases := []struct {
name string
rs Word
rt Word
funct uint32
expectLo Word
expectHi Word
expectPanic string
}{
// TODO(#12598): Fix 32-bit tests and remove these
{name: "mult", funct: uint32(0x18), rs: Word(0x0F_FF_00_00), rt: Word(100), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)},
{name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), expectHi: Word(0x0), expectLo: Word(0x1)},
{name: "mult", funct: uint32(0x18), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), expectHi: Word(0xE), expectLo: Word(0xFF_FF_FF_FF_FC_FC_FD_27)},
{name: "multu", funct: uint32(0x19), rs: Word(0x0F_FF_00_00), rt: Word(100), expectHi: Word(0x6), expectLo: Word(0x3F_9C_00_00)},
{name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), expectHi: Word(0xFF_FF_FF_FF_FF_FF_FF_FE), expectLo: Word(0x1)},
{name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_BE), expectHi: Word(0xFF_FF_FF_FF_AA_BB_CC_9F), expectLo: Word(0xFF_FF_FF_FF_FC_FD_02_9A)},
func TestEVM_SingleStep_MulDiv64(t *testing.T) {
cases := []mulDivTestCase{
// dmult s1, s2
// expected hi,lo were verified using qemu-mips
{name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0},
......@@ -514,9 +423,9 @@ func TestEVMSingleStep_DivMult64(t *testing.T) {
{name: "dmultu 14", funct: 0x1d, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x8F_FF_FF_FF_FF_FF_FF_FF), expectLo: 0xF0_00_00_00_00_00_00_01, expectHi: 0x47_FF_FF_FF_FF_FF_FF_FE},
// ddiv rs, rt
{name: "ddiv", funct: 0x1e, rs: 0, rt: 0, expectPanic: "instruction divide by zero"},
{name: "ddiv", funct: 0x1e, rs: 1, rt: 0, expectPanic: "instruction divide by zero"},
{name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"},
{name: "ddiv", funct: 0x1e, rs: 0, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "ddiv", funct: 0x1e, rs: 1, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "ddiv", funct: 0x1e, rs: 0, rt: 1, expectLo: 0, expectHi: 0},
{name: "ddiv", funct: 0x1e, rs: 1, rt: 1, expectLo: 1, expectHi: 0},
{name: "ddiv", funct: 0x1e, rs: 10, rt: 3, expectLo: 3, expectHi: 1},
......@@ -527,9 +436,9 @@ func TestEVMSingleStep_DivMult64(t *testing.T) {
{name: "ddiv", funct: 0x1e, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0x80_00_00_01_00_00_00_00, expectHi: 0},
// ddivu
{name: "ddivu", funct: 0x1f, rs: 0, rt: 0, expectPanic: "instruction divide by zero"},
{name: "ddivu", funct: 0x1f, rs: 1, rt: 0, expectPanic: "instruction divide by zero"},
{name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"},
{name: "ddivu", funct: 0x1f, rs: 0, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "ddivu", funct: 0x1f, rs: 1, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "ddivu", funct: 0x1f, rs: 0, rt: 1, expectLo: 0, expectHi: 0},
{name: "ddivu", funct: 0x1f, rs: 1, rt: 1, expectLo: 1, expectHi: 0},
{name: "ddivu", funct: 0x1f, rs: 10, rt: 3, expectLo: 3, expectHi: 1},
......@@ -540,57 +449,16 @@ func TestEVMSingleStep_DivMult64(t *testing.T) {
{name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: ^Word(0), expectLo: 0, expectHi: 0x7F_FF_FF_FF_00_00_00_00},
// a couple div/divu 64-bit edge cases
{name: "div lower word zero", funct: 0x1a, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"},
{name: "divu lower word zero", funct: 0x1b, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, expectPanic: "instruction divide by zero"},
{name: "div lower word zero", funct: 0x1a, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
{name: "divu lower word zero", funct: 0x1b, rs: 1, rt: 0xFF_FF_FF_FF_00_00_00_00, panicMsg: "instruction divide by zero", revertMsg: "division by zero"},
}
v := GetMultiThreadedTestCase(t)
for i, tt := range cases {
testName := fmt.Sprintf("%v %v", v.Name, tt.name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0))
state := goVm.GetState()
var rsReg uint32 = 17
var rtReg uint32 = 18
insn := rsReg<<21 | rtReg<<16 | tt.funct
state.GetRegistersRef()[rsReg] = tt.rs
state.GetRegistersRef()[rtReg] = tt.rt
testutil.StoreInstruction(state.GetMemory(), 0, insn)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
expected.LO = tt.expectLo
expected.HI = tt.expectHi
if tt.expectPanic != "" {
require.PanicsWithValue(t, tt.expectPanic, func() { _, _ = goVm.Step(true) })
// TODO(#12250): Assert EVM panic for divide by zero
// testutil.AssertEVMReverts(t, state, contracts, nil, proofData, errMsg)
} else {
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
}
})
}
testMulDiv(t, cases, false)
}
func TestEVMSingleStep_Branch64(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
pc Word
expectNextPC Word
opcode uint32
regimm uint32
expectLink bool
rs arch.SignedInteger
rt Word
offset uint16
}{
func TestEVM_SingleStep_Branch64(t *testing.T) {
t.Parallel()
cases := []branchTestCase{
// blez
{name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8},
{name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x18},
......@@ -635,34 +503,5 @@ func TestEVMSingleStep_Branch64(t *testing.T) {
{name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true},
}
for _, v := range versions {
for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc))
state := goVm.GetState()
const rsReg = 8 // t0
insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset)
testutil.StoreInstruction(state.GetMemory(), tt.pc, insn)
state.GetRegistersRef()[rsReg] = Word(tt.rs)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.Step += 1
expected.PC = state.GetCpu().NextPC
expected.NextPC = tt.expectNextPC
if tt.expectLink {
expected.Registers[31] = state.GetPC() + 8
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
testBranch(t, cases)
}
......@@ -20,7 +20,9 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)
func TestEVM(t *testing.T) {
func TestEVM_OpenMIPS(t *testing.T) {
testutil.Cannon32OnlyTest(t, "Skipping MIPS32 assembly test vectors for cannon64")
testFiles, err := os.ReadDir("open_mips_tests/test/bin")
require.NoError(t, err)
......@@ -36,7 +38,6 @@ func TestEVM(t *testing.T) {
for _, f := range testFiles {
testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name)
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
for _, skipped := range skipped {
if f.Name() == skipped {
t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name())
......@@ -105,7 +106,7 @@ func TestEVM(t *testing.T) {
}
}
func TestEVMSingleStep_Jump(t *testing.T) {
func TestEVM_SingleStep_Jump(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
......@@ -150,18 +151,8 @@ func TestEVMSingleStep_Jump(t *testing.T) {
}
}
func TestEVMSingleStep_Operators(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
isImm bool
rs Word
rt Word
imm uint16
funct uint32
opcode uint32
expectRes Word
}{
func TestEVM_SingleStep_Operators(t *testing.T) {
cases := []operatorTestCase{
{name: "add", funct: 0x20, isImm: false, rs: Word(12), rt: Word(20), expectRes: Word(32)}, // add t0, s1, s2
{name: "add", funct: 0x20, isImm: false, rs: ^Word(0), rt: ^Word(0), expectRes: Word(0xFF_FF_FF_FE)}, // add t0, s1, s2
{name: "add", funct: 0x20, isImm: false, rs: Word(0x7F_FF_FF_FF), rt: Word(0x7F_FF_FF_FF), expectRes: Word(0xFF_FF_FF_FE)}, // add t0, s1, s2
......@@ -191,7 +182,14 @@ func TestEVMSingleStep_Operators(t *testing.T) {
{name: "subu", funct: 0x23, isImm: false, rs: Word(20), rt: Word(12), expectRes: Word(8)}, // subu t0, s1, s2
{name: "subu", funct: 0x23, isImm: false, rs: ^Word(0), rt: Word(1), expectRes: Word(0xFF_FF_FF_FE)}, // subu t0, s1, s2
{name: "subu", funct: 0x23, isImm: false, rs: Word(1), rt: ^Word(0), expectRes: Word(0x2)}, // subu t0, s1, s2
}
testOperators(t, cases, true)
}
func TestEVM_SingleStep_Bitwise32(t *testing.T) {
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_Bitwise64")
// bitwise operations that use the full word size
cases := []operatorTestCase{
{name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2
{name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40
{name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2
......@@ -202,70 +200,15 @@ func TestEVMSingleStep_Operators(t *testing.T) {
{name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(1)}, // slt t0, s1, s2
{name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2
}
for _, v := range versions {
for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState()
var insn uint32
var baseReg uint32 = 17
var rtReg uint32
var rdReg uint32
if tt.isImm {
rtReg = 8
insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm)
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = tt.rs
} else {
rtReg = 18
rdReg = 8
insn = baseReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct
state.GetRegistersRef()[baseReg] = tt.rs
state.GetRegistersRef()[rtReg] = tt.rt
}
testutil.StoreInstruction(state.GetMemory(), 0, insn)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.Step += 1
expected.PC = 4
expected.NextPC = 8
if tt.isImm {
expected.Registers[rtReg] = tt.expectRes
} else {
expected.Registers[rdReg] = tt.expectRes
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
testOperators(t, cases, false)
}
func TestEVMSingleStep_LoadStore(t *testing.T) {
func TestEVM_SingleStep_LoadStore32(t *testing.T) {
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_LoadStore64")
loadMemVal := Word(0x11_22_33_44)
loadMemValNeg := Word(0xF1_F2_F3_F4)
rtVal := Word(0xaa_bb_cc_dd)
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
rt Word
base Word
imm uint32
opcode uint32
memVal Word
expectMemVal Word
expectRes Word
}{
cases := []loadStoreTestCase{
{name: "lb, offset=0", opcode: uint32(0x20), base: 0x100, imm: 0x20, memVal: loadMemVal, expectRes: 0x11},
{name: "lb, offset=1", opcode: uint32(0x20), base: 0x100, imm: 0x1, memVal: loadMemVal, expectRes: 0x22},
{name: "lb, offset=2", opcode: uint32(0x20), base: 0x100, imm: 0x2, memVal: loadMemVal, expectRes: 0x33},
......@@ -302,49 +245,10 @@ func TestEVMSingleStep_LoadStore(t *testing.T) {
{name: "sw", opcode: uint32(0x2b), base: 0x100, imm: 0x20, rt: rtVal, expectMemVal: 0xaa_bb_cc_dd},
{name: "swr unaligned", opcode: uint32(0x2e), base: 0x100, imm: 0x1, rt: rtVal, expectMemVal: 0xcc_dd_00_00},
}
var baseReg uint32 = 9
var rtReg uint32 = 8
for i, tt := range cases {
for _, v := range versions {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
addr := tt.base + Word(tt.imm)
effAddr := arch.AddressMask & addr
// Setup
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState()
insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.imm
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = tt.base
testutil.StoreInstruction(state.GetMemory(), 0, insn)
state.GetMemory().SetWord(effAddr, tt.memVal)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
if tt.expectMemVal != 0 {
expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal)
} else {
expected.Registers[rtReg] = tt.expectRes
}
// Run vm
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Validate
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
testLoadStore(t, cases)
}
func TestEVMSingleStep_MovzMovn(t *testing.T) {
func TestEVM_SingleStep_MovzMovn(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
......@@ -406,7 +310,7 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) {
}
func TestEVMSingleStep_MfhiMflo(t *testing.T) {
func TestEVM_SingleStep_MfhiMflo(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
......@@ -442,23 +346,8 @@ func TestEVMSingleStep_MfhiMflo(t *testing.T) {
}
}
func TestEVMSingleStep_MulDiv(t *testing.T) {
var tracer *tracing.Hooks
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
rs Word
rt Word
funct uint32
opcode uint32
expectHi Word
expectLo Word
expectRes Word
rdReg uint32
expectRevert string
errMsg string
}{
func TestEVM_SingleStep_MulDiv(t *testing.T) {
cases := []mulDivTestCase{
{name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(5), rt: Word(2), rdReg: uint32(0x8), expectRes: Word(10)}, // mul t0, t1, t2
{name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0x1), rt: ^Word(0), rdReg: uint32(0x8), expectRes: ^Word(0)}, // mul t1, t2
{name: "mul", funct: uint32(0x2), opcode: uint32(28), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x8), expectRes: Word(0x1)}, // mul t1, t2
......@@ -473,62 +362,18 @@ func TestEVMSingleStep_MulDiv(t *testing.T) {
{name: "multu", funct: uint32(0x19), rs: Word(0x1), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0x0), expectLo: Word(0xFF_FF_FF_FF)}, // multu t1, t2
{name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xFF_FF_FF_FE), expectLo: Word(0x1)}, // multu t1, t2
{name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xAA_BB_CC_BE), expectLo: Word(0xFC_FC_FD_27)}, // multu t1, t2
{name: "multu", funct: uint32(0x19), rs: Word(0xFF_FF_FF_D3), rt: Word(0xAA_BB_CC_BE), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(0xAA_BB_CC_9F), expectLo: Word(0xFC_FD_02_9A)}, // multu t1, t2
{name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // div t1, t2
{name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // div t1, t2
{name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // divu t1, t2
{name: "divu by zero", funct: uint32(0x1b), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), expectRevert: "instruction divide by zero", errMsg: "MIPS: division by zero"}, // divu t1, t2
{name: "div", funct: uint32(0x1a), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // div t1, t2
{name: "div by zero", funct: uint32(0x1a), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, // div t1, t2
{name: "divu", funct: uint32(0x1b), rs: Word(5), rt: Word(2), rdReg: uint32(0x0), opcode: uint32(0), expectHi: Word(1), expectLo: Word(2)}, // divu t1, t2
{name: "divu by zero", funct: uint32(0x1b), rs: Word(5), rt: Word(0), rdReg: uint32(0x0), opcode: uint32(0), panicMsg: "instruction divide by zero", revertMsg: "division by zero"}, // divu t1, t2
}
for _, v := range versions {
for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState()
var insn uint32
baseReg := uint32(0x9)
rtReg := uint32(0xa)
insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.rdReg<<11 | tt.funct
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = tt.rs
testutil.StoreInstruction(state.GetMemory(), 0, insn)
if tt.expectRevert != "" {
proofData := v.ProofGenerator(t, goVm.GetState())
require.PanicsWithValue(t, tt.expectRevert, func() {
_, _ = goVm.Step(
false)
})
testutil.AssertEVMReverts(t, state, v.Contracts, tracer, proofData, testutil.CreateErrorStringMatcher(tt.errMsg))
return
}
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
if tt.expectRes != 0 {
expected.Registers[tt.rdReg] = tt.expectRes
} else {
expected.HI = tt.expectHi
expected.LO = tt.expectLo
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
testMulDiv(t, cases, true)
}
func TestEVMSingleStep_MthiMtlo(t *testing.T) {
func TestEVM_SingleStep_MthiMtlo(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
......@@ -631,7 +476,7 @@ func TestEVM_MMap(t *testing.T) {
}
}
func TestEVMSysWriteHint(t *testing.T) {
func TestEVM_SysWriteHint(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
......@@ -824,7 +669,7 @@ func TestEVMSysWriteHint(t *testing.T) {
}
}
func TestEVMFault(t *testing.T) {
func TestEVM_Fault(t *testing.T) {
var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer
versions := GetMipsVersionTestCases(t)
......@@ -846,7 +691,7 @@ func TestEVMFault(t *testing.T) {
errMsg testutil.ErrMatcher
memoryProofAddresses []Word
}{
{name: "illegal instruction", nextPC: 0, insn: 0xFF_FF_FF_FF, errMsg: testutil.CreateErrorStringMatcher("invalid instruction"), memoryProofAddresses: []Word{0xa7ef00cc}},
{name: "illegal instruction", nextPC: 0, insn: 0b111110 << 26, errMsg: testutil.CreateErrorStringMatcher("invalid instruction"), memoryProofAddresses: []Word{0x0}}, // memoryProof for the zero address at register 0 (+ imm)
{name: "branch in delay-slot", nextPC: 8, insn: 0x11_02_00_03, errMsg: testutil.CreateErrorStringMatcher("branch in delay slot")},
{name: "jump in delay-slot", nextPC: 8, insn: 0x0c_00_00_0c, errMsg: testutil.CreateErrorStringMatcher("jump in delay slot")},
{name: "misaligned instruction", pc: 1, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()},
......@@ -859,7 +704,6 @@ func TestEVMFault(t *testing.T) {
for _, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC))
state := goVm.GetState()
testutil.StoreInstruction(state.GetMemory(), 0, tt.insn)
......@@ -874,7 +718,7 @@ func TestEVMFault(t *testing.T) {
}
}
func TestHelloEVM(t *testing.T) {
func TestEVM_HelloProgram(t *testing.T) {
if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
}
......@@ -921,7 +765,7 @@ func TestHelloEVM(t *testing.T) {
}
}
func TestClaimEVM(t *testing.T) {
func TestEVM_ClaimProgram(t *testing.T) {
if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
}
......@@ -966,7 +810,7 @@ func TestClaimEVM(t *testing.T) {
}
}
func TestEntryEVM(t *testing.T) {
func TestEVM_EntryProgram(t *testing.T) {
if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
}
......@@ -1010,19 +854,10 @@ func TestEntryEVM(t *testing.T) {
}
}
func TestEVMSingleStepBranch(t *testing.T) {
versions := GetMipsVersionTestCases(t)
cases := []struct {
name string
pc Word
expectNextPC Word
opcode uint32
regimm uint32
expectLink bool
rs arch.SignedInteger
rt Word
offset uint16
}{
func TestEVM_SingleStep_Branch32(t *testing.T) {
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_Branch64")
t.Parallel()
cases := []branchTestCase{
// blez
{name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8},
{name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18},
......@@ -1076,35 +911,5 @@ func TestEVMSingleStepBranch(t *testing.T) {
{name: "bgezal fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10, expectLink: true},
}
for _, v := range versions {
for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc))
state := goVm.GetState()
const rsReg = 8 // t0
insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset)
testutil.StoreInstruction(state.GetMemory(), tt.pc, insn)
state.GetRegistersRef()[rsReg] = Word(tt.rs)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.Step += 1
expected.PC = state.GetCpu().NextPC
expected.NextPC = tt.expectNextPC
if tt.expectLink {
expected.Registers[31] = state.GetPC() + 8
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
testBranch(t, cases)
}
......@@ -5,10 +5,13 @@
package tests
import (
"encoding/binary"
"fmt"
"slices"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
......@@ -360,3 +363,122 @@ func TestEVM_MT64_SCD(t *testing.T) {
}
}
}
func TestEVM_MT_SysRead_Preimage64(t *testing.T) {
preimageValue := make([]byte, 0, 8)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32)
prestateMem := Word(0xEE_EE_EE_EE_FF_FF_FF_FF)
cases := []testMTSysReadPreimageTestCase{
{name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_EE_EE_EE_FF_FF_FF_FF},
{name: "Aligned addr, write 2 byte", addr: 0x00_00_FF_00, count: 2, writeLen: 2, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_EE_EE_FF_FF_FF_FF},
{name: "Aligned addr, write 3 byte", addr: 0x00_00_FF_00, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_EE_FF_FF_FF_FF},
{name: "Aligned addr, write 4 byte", addr: 0x00_00_FF_00, count: 4, writeLen: 4, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_FF_FF_FF_FF},
{name: "Aligned addr, write 5 byte", addr: 0x00_00_FF_00, count: 5, writeLen: 5, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_FF_FF_FF},
{name: "Aligned addr, write 6 byte", addr: 0x00_00_FF_00, count: 6, writeLen: 6, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_FF_FF},
{name: "Aligned addr, write 7 byte", addr: 0x00_00_FF_00, count: 7, writeLen: 7, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_54_FF},
{name: "Aligned addr, write 8 byte", addr: 0x00_00_FF_00, count: 8, writeLen: 8, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_54_32},
{name: "1-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_01, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_EE_EE_FF_FF_FF_FF},
{name: "1-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_01, count: 2, writeLen: 2, preimageOffset: 9, prestateMem: prestateMem, postateMem: 0xEE_34_56_EE_FF_FF_FF_FF},
{name: "1-byte misaligned addr, write 3 byte", addr: 0x00_00_FF_01, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_FF_FF_FF_FF},
{name: "1-byte misaligned addr, write 4 byte", addr: 0x00_00_FF_01, count: 4, writeLen: 4, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_FF_FF_FF},
{name: "1-byte misaligned addr, write 5 byte", addr: 0x00_00_FF_01, count: 5, writeLen: 5, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_FF_FF},
{name: "1-byte misaligned addr, write 6 byte", addr: 0x00_00_FF_01, count: 6, writeLen: 6, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_76_FF},
{name: "1-byte misaligned addr, write 7 byte", addr: 0x00_00_FF_01, count: 7, writeLen: 7, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_76_54},
{name: "2-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_02, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_12_EE_FF_FF_FF_FF},
{name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 2, writeLen: 2, preimageOffset: 12, prestateMem: prestateMem, postateMem: 0xEE_EE_98_76_FF_FF_FF_FF},
{name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 3, writeLen: 3, preimageOffset: 12, prestateMem: prestateMem, postateMem: 0xEE_EE_98_76_54_FF_FF_FF},
{name: "2-byte misaligned addr, write 2 byte", addr: 0x00_00_FF_02, count: 4, writeLen: 4, preimageOffset: 12, prestateMem: prestateMem, postateMem: 0xEE_EE_98_76_54_32_FF_FF},
{name: "3-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_03, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_12_FF_FF_FF_FF},
{name: "4-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_04, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_12_FF_FF_FF},
{name: "5-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_05, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_12_FF_FF},
{name: "6-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_06, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_12_FF},
{name: "7-byte misaligned addr, write 1 byte", addr: 0x00_00_FF_07, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_12},
{name: "Count of 0", addr: 0x00_00_FF_03, count: 0, writeLen: 0, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_FF},
{name: "Count greater than 8", addr: 0x00_00_FF_00, count: 15, writeLen: 8, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0x12_34_56_78_98_76_54_32},
{name: "Count greater than 8, unaligned", addr: 0x00_00_FF_01, count: 15, writeLen: 7, preimageOffset: 8, prestateMem: prestateMem, postateMem: 0xEE_12_34_56_78_98_76_54},
{name: "Offset at last byte", addr: 0x00_00_FF_00, count: 8, writeLen: 1, preimageOffset: 15, prestateMem: prestateMem, postateMem: 0x32_EE_EE_EE_FF_FF_FF_FF},
{name: "Offset just out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 16, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_FF, shouldPanic: true},
{name: "Offset out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 17, prestateMem: prestateMem, postateMem: 0xEE_EE_EE_EE_FF_FF_FF_FF, shouldPanic: true},
}
testMTSysReadPreimage(t, preimageValue, cases)
}
func TestEVM_MT_StoreOpsClearMemReservation64(t *testing.T) {
t.Parallel()
cases := []testMTStoreOpsClearMemReservationTestCase{
{name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x78_FF_FF_FF_FF_FF_FF_FF},
{name: "Store byte lower", opcode: 0b10_1000, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_78_FF_FF_FF},
{name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x56_78_FF_FF_FF_FF_FF_FF},
{name: "Store halfword lower", opcode: 0b10_1001, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_56_78_FF_FF},
{name: "Store word left", opcode: 0b10_1010, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x12_34_56_78_FF_FF_FF_FF},
{name: "Store word left lower", opcode: 0b10_1010, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_12_34_56_78},
{name: "Store word", opcode: 0b10_1011, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x12_34_56_78_FF_FF_FF_FF},
{name: "Store word lower", opcode: 0b10_1011, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_12_34_56_78},
{name: "Store word right", opcode: 0b10_1110, base: 0xFF_00_00_00, offset: 0x10, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0x78_FF_FF_FF_FF_FF_FF_FF},
{name: "Store word right lower", opcode: 0b10_1110, base: 0xFF_00_00_00, offset: 0x14, effAddr: 0xFF_00_00_10, preMem: ^Word(0), postMem: 0xFF_FF_FF_FF_78_FF_FF_FF},
}
testMTStoreOpsClearMemReservation(t, cases)
}
var NoopSyscalls64 = map[string]uint32{
"SysMunmap": 5011,
"SysGetAffinity": 5196,
"SysMadvise": 5027,
"SysRtSigprocmask": 5014,
"SysSigaltstack": 5129,
"SysRtSigaction": 5013,
"SysPrlimit64": 5297,
"SysClose": 5003,
"SysPread64": 5016,
"SysStat": 5004,
"SysFstat": 5005,
//"SysFstat64": UndefinedSysNr,
"SysOpenAt": 5247,
"SysReadlink": 5087,
"SysReadlinkAt": 5257,
"SysIoctl": 5015,
"SysEpollCreate1": 5285,
"SysPipe2": 5287,
"SysEpollCtl": 5208,
"SysEpollPwait": 5272,
"SysGetRandom": 5313,
"SysUname": 5061,
//"SysStat64": UndefinedSysNr,
"SysGetuid": 5100,
"SysGetgid": 5102,
//"SysLlseek": UndefinedSysNr,
"SysMinCore": 5026,
"SysTgkill": 5225,
"SysGetRLimit": 5095,
"SysLseek": 5008,
"SysSetITimer": 5036,
"SysTimerCreate": 5216,
"SysTimerSetTime": 5217,
"SysTimerDelete": 5220,
}
func TestEVM_NoopSyscall64(t *testing.T) {
testNoopSyscall(t, NoopSyscalls64)
}
func TestEVM_UnsupportedSyscall64(t *testing.T) {
t.Parallel()
var noopSyscallNums = maps.Values(NoopSyscalls64)
var SupportedSyscalls = []uint32{arch.SysMmap, arch.SysBrk, arch.SysClone, arch.SysExitGroup, arch.SysRead, arch.SysWrite, arch.SysFcntl, arch.SysExit, arch.SysSchedYield, arch.SysGetTID, arch.SysFutex, arch.SysOpen, arch.SysNanosleep, arch.SysClockGetTime, arch.SysGetpid}
unsupportedSyscalls := make([]uint32, 0, 400)
for i := 5000; i < 5400; i++ {
candidate := uint32(i)
if slices.Contains(SupportedSyscalls, candidate) || slices.Contains(noopSyscallNums, candidate) {
continue
}
unsupportedSyscalls = append(unsupportedSyscalls, candidate)
}
testUnsupportedSyscall(t, unsupportedSyscalls)
}
......@@ -9,7 +9,6 @@ import (
"testing"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
......@@ -19,7 +18,6 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
)
type Word = arch.Word
......@@ -190,37 +188,14 @@ func TestEVM_MT_SC(t *testing.T) {
}
}
func TestEVM_MT_SysRead_Preimage(t *testing.T) {
func TestEVM_MT_SysRead_Preimage32(t *testing.T) {
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_MT_SysRead_Preimage64")
t.Parallel()
preimageValue := make([]byte, 0, 8)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32)
llVariations := []struct {
name string
llReservationStatus multithreaded.LLReservationStatus
matchThreadId bool
effAddrOffset Word
shouldClearReservation bool
}{
{name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true},
{name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
{name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false},
{name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true},
{name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
}
cases := []struct {
name string
addr Word
count Word
writeLen Word
preimageOffset Word
prestateMem Word
postateMem Word
shouldPanic bool
}{
cases := []testMTSysReadPreimageTestCase{
{name: "Aligned addr, write 1 byte", addr: 0x00_00_FF_00, count: 1, writeLen: 1, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_FF_FF_FF},
{name: "Aligned addr, write 2 byte", addr: 0x00_00_FF_00, count: 2, writeLen: 2, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_FF_FF},
{name: "Aligned addr, write 3 byte", addr: 0x00_00_FF_00, count: 3, writeLen: 3, preimageOffset: 8, prestateMem: 0xFF_FF_FF_FF, postateMem: 0x12_34_56_FF},
......@@ -238,147 +213,22 @@ func TestEVM_MT_SysRead_Preimage(t *testing.T) {
{name: "Offset just out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 16, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF, shouldPanic: true},
{name: "Offset out of bounds", addr: 0x00_00_FF_00, count: 4, writeLen: 0, preimageOffset: 17, prestateMem: 0xFF_FF_FF_FF, postateMem: 0xFF_FF_FF_FF, shouldPanic: true},
}
for i, c := range cases {
for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
effAddr := arch.AddressMask & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue)
goVm, state, contracts := setup(t, i, oracle)
step := state.GetStep()
// Define LL-related params
llAddress := effAddr + v.effAddrOffset
llOwnerThread := state.GetCurrentThread().ThreadId
if !v.matchThreadId {
llOwnerThread += 1
}
// Set up state
state.PreimageKey = preimageKey
state.PreimageOffset = c.preimageOffset
state.GetRegistersRef()[2] = arch.SysRead
state.GetRegistersRef()[4] = exec.FdPreimageRead
state.GetRegistersRef()[5] = c.addr
state.GetRegistersRef()[6] = c.count
testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn)
state.LLReservationStatus = v.llReservationStatus
state.LLAddress = llAddress
state.LLOwnerThread = llOwnerThread
state.GetMemory().SetWord(effAddr, c.prestateMem)
// Setup expectations
expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep()
expected.ActiveThread().Registers[2] = c.writeLen
expected.ActiveThread().Registers[7] = 0 // no error
expected.PreimageOffset += c.writeLen
expected.ExpectMemoryWordWrite(effAddr, c.postateMem)
if v.shouldClearReservation {
expected.LLReservationStatus = multithreaded.LLStatusNone
expected.LLAddress = 0
expected.LLOwnerThread = 0
}
if c.shouldPanic {
require.Panics(t, func() { _, _ = goVm.Step(true) })
testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, contracts)
} else {
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
}
})
}
}
testMTSysReadPreimage(t, preimageValue, cases)
}
func TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) {
llVariations := []struct {
name string
llReservationStatus multithreaded.LLReservationStatus
matchThreadId bool
effAddrOffset Word
shouldClearReservation bool
}{
{name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true},
{name: "matching reservation, diff thread, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, shouldClearReservation: true},
{name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
{name: "mismatched reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false},
{name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true},
{name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
}
rt := Word(0x12_34_56_78)
baseReg := 5
rtReg := 6
cases := []struct {
name string
opcode int
offset int
base Word
effAddr Word
preMem Word
postMem Word
}{
{name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF},
{name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x56_78_FF_FF},
{name: "Store word left", opcode: 0b10_1010, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78},
{name: "Store word", opcode: 0b10_1011, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78},
{name: "Store word right", opcode: 0b10_1110, base: 0xFF_00_00_04, offset: 0xFF_00_00_08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF},
}
for i, c := range cases {
for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08))
step := state.GetStep()
// Define LL-related params
llAddress := c.effAddr + v.effAddrOffset
llOwnerThread := state.GetCurrentThread().ThreadId
if !v.matchThreadId {
llOwnerThread += 1
}
// Setup state
state.GetRegistersRef()[rtReg] = rt
state.GetRegistersRef()[baseReg] = c.base
testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn)
state.GetMemory().SetWord(c.effAddr, c.preMem)
state.LLReservationStatus = v.llReservationStatus
state.LLAddress = llAddress
state.LLOwnerThread = llOwnerThread
// Setup expectations
expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep()
expected.ExpectMemoryWordWrite(c.effAddr, c.postMem)
if v.shouldClearReservation {
expected.LLReservationStatus = multithreaded.LLStatusNone
expected.LLAddress = 0
expected.LLOwnerThread = 0
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
})
}
func TestEVM_MT_StoreOpsClearMemReservation32(t *testing.T) {
t.Parallel()
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_MT_StoreOpsClearMemReservation64")
cases := []testMTStoreOpsClearMemReservationTestCase{
{name: "Store byte", opcode: 0b10_1000, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF},
{name: "Store halfword", opcode: 0b10_1001, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x56_78_FF_FF},
{name: "Store word left", opcode: 0b10_1010, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78},
{name: "Store word", opcode: 0b10_1011, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x12_34_56_78},
{name: "Store word right", opcode: 0b10_1110, base: 0xFF_00_00_04, offset: 0x08, effAddr: 0xFF_00_00_0C, preMem: 0xFF_FF_FF_FF, postMem: 0x78_FF_FF_FF},
}
testMTStoreOpsClearMemReservation(t, cases)
}
func TestEVM_SysClone_FlagHandling(t *testing.T) {
......@@ -633,39 +483,38 @@ func TestEVM_PopExitedThread(t *testing.T) {
}
func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
// Note: parameters are written as 64-bit values. For 32-bit architectures, these values are downcast to 32-bit
cases := []struct {
name string
addressParam Word
effAddr Word
targetValue Word
actualValue Word
timeout Word
addressParam uint64
effAddr uint64
targetValue uint64
actualValue uint64
timeout uint64
shouldFail bool
shouldSetTimeout bool
}{
{name: "successful wait, no timeout", addressParam: 0x1234, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01},
{name: "successful wait, no timeout, unaligned addr", addressParam: 0x1235, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01},
{name: "memory mismatch, no timeout", addressParam: 0x1200, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, shouldFail: true},
{name: "memory mismatch, no timeout, unaligned", addressParam: 0x1203, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, shouldFail: true},
{name: "successful wait w timeout", addressParam: 0x1234, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01, timeout: 1000000, shouldSetTimeout: true},
{name: "successful wait w timeout, unaligned", addressParam: 0x1232, effAddr: 0x1230, targetValue: 0x01, actualValue: 0x01, timeout: 1000000, shouldSetTimeout: true},
{name: "memory mismatch w timeout", addressParam: 0x1200, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, timeout: 2000000, shouldFail: true},
{name: "memory mismatch w timeout, unaligned", addressParam: 0x120F, effAddr: 0x120C, targetValue: 0x01, actualValue: 0x02, timeout: 2000000, shouldFail: true},
{name: "successful wait, no timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_38, effAddr: 0xFF_FF_FF_FF_FF_FF_12_38, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01},
{name: "successful wait, no timeout, unaligned addr", addressParam: 0xFF_FF_FF_FF_FF_FF_12_39, effAddr: 0xFF_FF_FF_FF_FF_FF_12_38, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01},
{name: "memory mismatch, no timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_00, effAddr: 0xFF_FF_FF_FF_FF_FF_12_00, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, shouldFail: true},
{name: "memory mismatch, no timeout, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_12_03, effAddr: 0xFF_FF_FF_FF_FF_FF_12_00, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, shouldFail: true},
{name: "successful wait w timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_38, effAddr: 0xFF_FF_FF_FF_FF_FF_12_38, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01, timeout: 1000000, shouldSetTimeout: true},
{name: "successful wait w timeout, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_12_32, effAddr: 0xFF_FF_FF_FF_FF_FF_12_30, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_01, timeout: 1000000, shouldSetTimeout: true},
{name: "memory mismatch w timeout", addressParam: 0xFF_FF_FF_FF_FF_FF_12_00, effAddr: 0xFF_FF_FF_FF_FF_FF_12_00, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, timeout: 2000000, shouldFail: true},
{name: "memory mismatch w timeout, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_12_0F, effAddr: 0xFF_FF_FF_FF_FF_FF_12_10, targetValue: 0xFF_FF_FF_FF_FF_FF_FF_01, actualValue: 0xFF_FF_FF_FF_FF_FF_FF_02, timeout: 2000000, shouldFail: true},
}
for i, c := range cases {
t.Run(c.name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, i*1234, nil)
step := state.GetStep()
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.Memory.SetWord(c.effAddr, c.actualValue)
state.Memory.SetWord(Word(c.effAddr), Word(c.actualValue))
state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number
state.GetRegistersRef()[4] = c.addressParam
state.GetRegistersRef()[4] = Word(c.addressParam)
state.GetRegistersRef()[5] = exec.FutexWaitPrivate
state.GetRegistersRef()[6] = c.targetValue
state.GetRegistersRef()[7] = c.timeout
state.GetRegistersRef()[6] = Word(c.targetValue)
state.GetRegistersRef()[7] = Word(c.timeout)
// Setup expectations
expected := mttestutil.NewExpectedMTState(state)
......@@ -678,8 +527,8 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
expected.ActiveThread().Registers[7] = exec.MipsEAGAIN
} else {
// PC and return registers should not update on success, updates happen when wait completes
expected.ActiveThread().FutexAddr = c.effAddr
expected.ActiveThread().FutexVal = c.targetValue
expected.ActiveThread().FutexAddr = Word(c.effAddr)
expected.ActiveThread().FutexVal = Word(c.targetValue)
expected.ActiveThread().FutexTimeoutStep = exec.FutexNoTimeout
if c.shouldSetTimeout {
expected.ActiveThread().FutexTimeoutStep = step + exec.FutexTimeoutSteps + 1
......@@ -687,9 +536,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
}
// State transition
var err error
var stepWitness *mipsevm.StepWitness
stepWitness, err = goVm.Step(true)
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Validate post-state
......@@ -700,39 +547,38 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
}
func TestEVM_SysFutex_WakePrivate(t *testing.T) {
// Note: parameters are written as 64-bit values. For 32-bit architectures, these values are downcast to 32-bit
cases := []struct {
name string
addressParam Word
effAddr Word
addressParam uint64
effAddr uint64
activeThreadCount int
inactiveThreadCount int
traverseRight bool
expectTraverseRight bool
}{
{name: "Traverse right", addressParam: 0x6700, effAddr: 0x6700, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true},
{name: "Traverse right, unaligned addr", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true},
{name: "Traverse right, no left threads", addressParam: 0x6784, effAddr: 0x6784, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse right, no left threads, unaligned addr", addressParam: 0x678E, effAddr: 0x678C, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse right, single thread", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse right, single thread, unaligned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse left", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false},
{name: "Traverse left, unaliagned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false},
{name: "Traverse left, switch directions", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true},
{name: "Traverse left, switch directions, unaligned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true},
{name: "Traverse left, single thread", addressParam: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true},
{name: "Traverse left, single thread, unaligned", addressParam: 0x6789, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true},
{name: "Traverse right", addressParam: 0xFF_FF_FF_FF_FF_FF_67_00, effAddr: 0xFF_FF_FF_FF_FF_FF_67_00, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true},
{name: "Traverse right, unaligned addr", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: true},
{name: "Traverse right, no left threads", addressParam: 0xFF_FF_FF_FF_FF_FF_67_84, effAddr: 0xFF_FF_FF_FF_FF_FF_67_84, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse right, no left threads, unaligned addr", addressParam: 0xFF_FF_FF_FF_FF_FF_67_8E, effAddr: 0xFF_FF_FF_FF_FF_FF_67_8C, activeThreadCount: 2, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse right, single thread", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse right, single thread, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: true},
{name: "Traverse left", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false},
{name: "Traverse left, unaliagned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false},
{name: "Traverse left, switch directions", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true},
{name: "Traverse left, switch directions, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true},
{name: "Traverse left, single thread", addressParam: 0xFF_FF_FF_FF_FF_FF_67_88, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true},
{name: "Traverse left, single thread, unaligned", addressParam: 0xFF_FF_FF_FF_FF_FF_67_89, effAddr: 0xFF_FF_FF_FF_FF_FF_67_88, activeThreadCount: 1, inactiveThreadCount: 0, traverseRight: false, expectTraverseRight: true},
}
for i, c := range cases {
t.Run(c.name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, i*1122, nil)
mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount)
step := state.Step
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number
state.GetRegistersRef()[4] = c.addressParam
state.GetRegistersRef()[4] = Word(c.addressParam)
state.GetRegistersRef()[5] = exec.FutexWakePrivate
// Set up post-state expectations
......@@ -740,7 +586,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
expected.ExpectStep()
expected.ActiveThread().Registers[2] = 0
expected.ActiveThread().Registers[7] = 0
expected.Wakeup = c.effAddr
expected.Wakeup = Word(c.effAddr) & arch.AddressMask // aligned for 32 and 64-bit compatibility
expected.ExpectPreemption(state)
expected.TraverseRight = c.expectTraverseRight
if c.traverseRight != c.expectTraverseRight {
......@@ -750,9 +596,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
}
// State transition
var err error
var stepWitness *mipsevm.StepWitness
stepWitness, err = goVm.Step(true)
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Validate post-state
......@@ -760,6 +604,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
})
}
}
func TestEVM_SysFutex_UnsupportedOp(t *testing.T) {
......@@ -1103,70 +948,27 @@ var NoopSyscalls = map[string]uint32{
"SysTimerDelete": 4261,
}
func TestEVM_NoopSyscall(t *testing.T) {
for noopName, noopVal := range NoopSyscalls {
t.Run(noopName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, int(noopVal), nil)
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = Word(noopVal) // Set syscall number
step := state.Step
// Set up post-state expectations
expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep()
expected.ActiveThread().Registers[2] = 0
expected.ActiveThread().Registers[7] = 0
// State transition
var err error
var stepWitness *mipsevm.StepWitness
stepWitness, err = goVm.Step(true)
require.NoError(t, err)
// Validate post-state
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
})
}
func TestEVM_NoopSyscall32(t *testing.T) {
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_NoopSyscall64")
testNoopSyscall(t, NoopSyscalls)
}
func TestEVM_UnsupportedSyscall(t *testing.T) {
func TestEVM_UnsupportedSyscall32(t *testing.T) {
testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_UnsupportedSyscall64")
t.Parallel()
var tracer *tracing.Hooks
var NoopSyscallNums = maps.Values(NoopSyscalls)
var SupportedSyscalls = []uint32{arch.SysMmap, arch.SysBrk, arch.SysClone, arch.SysExitGroup, arch.SysRead, arch.SysWrite, arch.SysFcntl, arch.SysExit, arch.SysSchedYield, arch.SysGetTID, arch.SysFutex, arch.SysOpen, arch.SysNanosleep, arch.SysClockGetTime, arch.SysGetpid}
var noopSyscallNums = maps.Values(NoopSyscalls)
var supportedSyscalls = []uint32{arch.SysMmap, arch.SysBrk, arch.SysClone, arch.SysExitGroup, arch.SysRead, arch.SysWrite, arch.SysFcntl, arch.SysExit, arch.SysSchedYield, arch.SysGetTID, arch.SysFutex, arch.SysOpen, arch.SysNanosleep, arch.SysClockGetTime, arch.SysGetpid}
unsupportedSyscalls := make([]uint32, 0, 400)
for i := 4000; i < 4400; i++ {
candidate := uint32(i)
if slices.Contains(SupportedSyscalls, candidate) || slices.Contains(NoopSyscallNums, candidate) {
if slices.Contains(supportedSyscalls, candidate) || slices.Contains(noopSyscallNums, candidate) {
continue
}
unsupportedSyscalls = append(unsupportedSyscalls, candidate)
}
for i, syscallNum := range unsupportedSyscalls {
testName := fmt.Sprintf("Unsupported syscallNum %v", syscallNum)
i := i
syscallNum := syscallNum
t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
t.Parallel()
goVm, state, contracts := setup(t, i*3434, nil)
// Setup basic getThreadId syscall instruction
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = Word(syscallNum)
proofData := multiThreadedProofGenerator(t, state)
// Set up post-state expectations
require.Panics(t, func() { _, _ = goVm.Step(true) })
errorMessage := "MIPS2: unimplemented syscall"
testutil.AssertEVMReverts(t, state, contracts, tracer, proofData, testutil.CreateErrorStringMatcher(errorMessage))
})
}
testUnsupportedSyscall(t, unsupportedSyscalls)
}
func TestEVM_EmptyThreadStacks(t *testing.T) {
......
......@@ -19,20 +19,11 @@ import (
const syscallInsn = uint32(0x00_00_00_0c)
func FuzzStateSyscallBrk32(f *testing.F) {
doFuzzStateSyscallBrk(f)
}
func FuzzStateSyscallBrk64(f *testing.F) {
doFuzzStateSyscallBrk(f)
}
func doFuzzStateSyscallBrk(f *testing.F) {
func FuzzStateSyscallBrk(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := goVm.GetState()
state.GetRegistersRef()[2] = arch.SysBrk
......@@ -57,15 +48,7 @@ func doFuzzStateSyscallBrk(f *testing.F) {
})
}
func FuzzStateSyscallMmap32(f *testing.F) {
doFuzzStateSyscallMmap(f)
}
func FuzzStateSyscallMmap64(f *testing.F) {
doFuzzStateSyscallMmap(f)
}
func doFuzzStateSyscallMmap(f *testing.F) {
func FuzzStateSyscallMmap(f *testing.F) {
// Add special cases for large memory allocation
f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1))
f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2))
......@@ -76,7 +59,6 @@ func doFuzzStateSyscallMmap(f *testing.F) {
f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed), testutil.WithHeap(heap))
state := goVm.GetState()
......@@ -121,20 +103,11 @@ func doFuzzStateSyscallMmap(f *testing.F) {
})
}
func FuzzStateSyscallExitGroup32(f *testing.F) {
doFuzzStateSyscallExitGroup(f)
}
func FuzzStateSyscallExitGroup64(f *testing.F) {
doFuzzStateSyscallExitGroup(f)
}
func doFuzzStateSyscallExitGroup(f *testing.F) {
func FuzzStateSyscallExitGroup(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed))
state := goVm.GetState()
......@@ -159,20 +132,11 @@ func doFuzzStateSyscallExitGroup(f *testing.F) {
})
}
func FuzzStateSyscallFcntl32(f *testing.F) {
doFuzzStateSyscallFcntl(f)
}
func FuzzStateSyscallFcntl64(f *testing.F) {
doFuzzStateSyscallFcntl(f)
}
func doFuzzStateSyscallFcntl(f *testing.F) {
func FuzzStateSyscallFcntl(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed))
state := goVm.GetState()
......@@ -193,7 +157,7 @@ func doFuzzStateSyscallFcntl(f *testing.F) {
expected.Registers[2] = 0
expected.Registers[7] = 0
default:
expected.Registers[2] = 0xFF_FF_FF_FF
expected.Registers[2] = ^Word(0)
expected.Registers[7] = exec.MipsEBADF
}
} else if cmd == 3 {
......@@ -205,11 +169,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) {
expected.Registers[2] = 1
expected.Registers[7] = 0
default:
expected.Registers[2] = 0xFF_FF_FF_FF
expected.Registers[2] = ^Word(0)
expected.Registers[7] = exec.MipsEBADF
}
} else {
expected.Registers[2] = 0xFF_FF_FF_FF
expected.Registers[2] = ^Word(0)
expected.Registers[7] = exec.MipsEINVAL
}
......@@ -224,20 +188,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) {
})
}
func FuzzStateHintRead32(f *testing.F) {
doFuzzStateHintRead(f)
}
func FuzzStateHintRead64(f *testing.F) {
doFuzzStateHintRead(f)
}
func doFuzzStateHintRead(f *testing.F) {
func FuzzStateHintRead(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
......@@ -270,15 +225,7 @@ func doFuzzStateHintRead(f *testing.F) {
})
}
func FuzzStatePreimageRead32(f *testing.F) {
doFuzzStatePreimageRead(f)
}
func FuzzStatePreimageRead64(f *testing.F) {
doFuzzStatePreimageRead(f)
}
func doFuzzStatePreimageRead(f *testing.F) {
func FuzzStatePreimageRead(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) {
for _, v := range versions {
......@@ -342,20 +289,11 @@ func doFuzzStatePreimageRead(f *testing.F) {
})
}
func FuzzStateHintWrite32(f *testing.F) {
doFuzzStateHintWrite(f)
}
func FuzzStateHintWrite64(f *testing.F) {
doFuzzStateHintWrite(f)
}
func doFuzzStateHintWrite(f *testing.F) {
func FuzzStateHintWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
// Make sure pc does not overlap with hint data in memory
pc := Word(0)
if addr <= 8 {
......@@ -433,27 +371,18 @@ func doFuzzStateHintWrite(f *testing.F) {
})
}
func FuzzStatePreimageWrite32(f *testing.F) {
doFuzzStatePreimageWrite(f)
}
func FuzzStatePreimageWrite64(f *testing.F) {
doFuzzStatePreimageWrite(f)
}
func doFuzzStatePreimageWrite(f *testing.F) {
func FuzzStatePreimageWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) {
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
// Make sure pc does not overlap with preimage data in memory
pc := Word(0)
if addr <= 8 {
addr += 8
}
effAddr := addr & arch.AddressMask
preexistingMemoryVal := [4]byte{0x12, 0x34, 0x56, 0x78}
preexistingMemoryVal := [8]byte{0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21}
preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData)
......@@ -471,7 +400,7 @@ func doFuzzStatePreimageWrite(f *testing.F) {
expectBytesWritten := count
alignment := addr & arch.ExtMask
sz := 4 - alignment
sz := arch.WordSizeBytes - alignment
if sz < expectBytesWritten {
expectBytesWritten = sz
}
......
......@@ -13,18 +13,9 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
)
func FuzzStateSyscallCloneMT32(f *testing.F) {
doFuzzStateSyscallCloneMT(f)
}
func FuzzStateSyscallCloneMT64(f *testing.F) {
doFuzzStateSyscallCloneMT(f)
}
func doFuzzStateSyscallCloneMT(f *testing.F) {
func FuzzStateSyscallCloneMT(f *testing.F) {
v := GetMultiThreadedTestCase(f)
f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := mttestutil.GetMtState(t, goVm)
// Update existing threads to avoid collision with nextThreadId
......
package tests
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum/go-ethereum/crypto"
)
type operatorTestCase struct {
name string
isImm bool
rs Word
rt Word
imm uint16
funct uint32
opcode uint32
expectRes Word
}
func testOperators(t *testing.T, cases []operatorTestCase, mips32Insn bool) {
versions := GetMipsVersionTestCases(t)
for _, v := range versions {
for i, tt := range cases {
// sign extend inputs for 64-bit compatibility
if mips32Insn {
tt.rs = randomizeUpperWord(signExtend64(tt.rs))
tt.rt = randomizeUpperWord(signExtend64(tt.rt))
tt.expectRes = signExtend64(tt.expectRes)
}
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
validator := testutil.NewEvmValidator(t, v.StateHashFn, v.Contracts)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState()
var insn uint32
var baseReg uint32 = 17
var rtReg uint32
var rdReg uint32
if tt.isImm {
rtReg = 8
insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm)
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = tt.rs
} else {
rtReg = 18
rdReg = 8
insn = baseReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct
state.GetRegistersRef()[baseReg] = tt.rs
state.GetRegistersRef()[rtReg] = tt.rt
}
testutil.StoreInstruction(state.GetMemory(), 0, insn)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.Step += 1
expected.PC = 4
expected.NextPC = 8
if tt.isImm {
expected.Registers[rtReg] = tt.expectRes
} else {
expected.Registers[rdReg] = tt.expectRes
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
validator.ValidateEVM(t, stepWitness, step, goVm)
})
}
}
}
type mulDivTestCase struct {
name string
rs Word
rt Word
funct uint32
opcode uint32
expectHi Word
expectLo Word
expectRes Word
rdReg uint32
panicMsg string
revertMsg string
}
func testMulDiv(t *testing.T, cases []mulDivTestCase, mips32Insn bool) {
versions := GetMipsVersionTestCases(t)
for _, v := range versions {
for i, tt := range cases {
if mips32Insn {
tt.rs = randomizeUpperWord(signExtend64(tt.rs))
tt.rt = randomizeUpperWord(signExtend64(tt.rt))
tt.expectHi = signExtend64(tt.expectHi)
tt.expectLo = signExtend64(tt.expectLo)
tt.expectRes = signExtend64(tt.expectRes)
}
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPC(0), testutil.WithNextPC(4))
state := goVm.GetState()
var insn uint32
baseReg := uint32(0x9)
rtReg := uint32(0xa)
insn = tt.opcode<<26 | baseReg<<21 | rtReg<<16 | tt.rdReg<<11 | tt.funct
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = tt.rs
testutil.StoreInstruction(state.GetMemory(), 0, insn)
if tt.panicMsg != "" {
proofData := v.ProofGenerator(t, goVm.GetState())
require.PanicsWithValue(t, tt.panicMsg, func() {
_, _ = goVm.Step(
false)
})
testutil.AssertEVMReverts(t, state, v.Contracts, nil, proofData, testutil.CreateErrorStringMatcher(tt.revertMsg))
return
}
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
if tt.expectRes != 0 {
expected.Registers[tt.rdReg] = tt.expectRes
} else {
expected.HI = tt.expectHi
expected.LO = tt.expectLo
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
}
type loadStoreTestCase struct {
name string
rt Word
base Word
imm uint32
opcode uint32
memVal Word
expectMemVal Word
expectRes Word
}
func testLoadStore(t *testing.T, cases []loadStoreTestCase) {
baseReg := uint32(9)
rtReg := uint32(8)
v := GetMultiThreadedTestCase(t)
for i, tt := range cases {
testName := fmt.Sprintf("%v %v", v.Name, tt.name)
t.Run(testName, func(t *testing.T) {
addr := tt.base + Word(tt.imm)
effAddr := arch.AddressMask & addr
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0))
state := goVm.GetState()
insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm)
state.GetRegistersRef()[rtReg] = tt.rt
state.GetRegistersRef()[baseReg] = tt.base
testutil.StoreInstruction(state.GetMemory(), 0, insn)
state.GetMemory().SetWord(effAddr, tt.memVal)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
if tt.expectMemVal != 0 {
expected.ExpectMemoryWriteWord(effAddr, tt.expectMemVal)
} else {
expected.Registers[rtReg] = tt.expectRes
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
type branchTestCase struct {
name string
pc Word
expectNextPC Word
opcode uint32
regimm uint32
expectLink bool
rs arch.SignedInteger
offset uint16
}
func testBranch(t *testing.T, cases []branchTestCase) {
versions := GetMipsVersionTestCases(t)
for _, v := range versions {
for i, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) {
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(tt.pc))
state := goVm.GetState()
const rsReg = 8 // t0
insn := tt.opcode<<26 | rsReg<<21 | tt.regimm<<16 | uint32(tt.offset)
testutil.StoreInstruction(state.GetMemory(), tt.pc, insn)
state.GetRegistersRef()[rsReg] = Word(tt.rs)
step := state.GetStep()
// Setup expectations
expected := testutil.NewExpectedState(state)
expected.Step += 1
expected.PC = state.GetCpu().NextPC
expected.NextPC = tt.expectNextPC
if tt.expectLink {
expected.Registers[31] = state.GetPC() + 8
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
})
}
}
}
type testMTStoreOpsClearMemReservationTestCase struct {
// name is the test name
name string
// opcode is the instruction opcode
opcode uint32
// offset is the immediate offset encoded in the instruction
offset uint32
// base is the base/rs register prestate
base Word
// effAddr is the address used to set the prestate preMem value. It is also used as the base LLAddress that can be adjusted reservation assertions
effAddr Word
// premem is the prestate value of the word located at effrAddr
preMem Word
// postMem is the expected post-state value of the word located at effAddr
postMem Word
}
func testMTStoreOpsClearMemReservation(t *testing.T, cases []testMTStoreOpsClearMemReservationTestCase) {
llVariations := []struct {
name string
llReservationStatus multithreaded.LLReservationStatus
matchThreadId bool
effAddrOffset Word
shouldClearReservation bool
}{
{name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true},
{name: "matching reservation, diff thread, 64-bit", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, shouldClearReservation: true},
{name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
{name: "mismatched reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false},
{name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true},
{name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
}
rt := Word(0x12_34_56_78)
//rt := Word(0x12_34_56_78_12_34_56_78)
baseReg := uint32(5)
rtReg := uint32(6)
for i, c := range cases {
for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) {
t.Parallel()
insn := uint32((c.opcode << 26) | (baseReg & 0x1F << 21) | (rtReg & 0x1F << 16) | (0xFFFF & c.offset))
goVm, state, contracts := setup(t, i, nil, testutil.WithPCAndNextPC(0x08))
step := state.GetStep()
// Define LL-related params
llAddress := c.effAddr + v.effAddrOffset
llOwnerThread := state.GetCurrentThread().ThreadId
if !v.matchThreadId {
llOwnerThread += 1
}
// Setup state
state.GetRegistersRef()[rtReg] = rt
state.GetRegistersRef()[baseReg] = c.base
testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn)
state.GetMemory().SetWord(c.effAddr, c.preMem)
state.LLReservationStatus = v.llReservationStatus
state.LLAddress = llAddress
state.LLOwnerThread = llOwnerThread
// Setup expectations
expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep()
expected.ExpectMemoryWordWrite(c.effAddr, c.postMem)
if v.shouldClearReservation {
expected.LLReservationStatus = multithreaded.LLStatusNone
expected.LLAddress = 0
expected.LLOwnerThread = 0
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
})
}
}
}
type testMTSysReadPreimageTestCase struct {
name string
addr Word
count Word
writeLen Word
preimageOffset Word
prestateMem Word
postateMem Word
shouldPanic bool
}
func testMTSysReadPreimage(t *testing.T, preimageValue []byte, cases []testMTSysReadPreimageTestCase) {
llVariations := []struct {
name string
llReservationStatus multithreaded.LLReservationStatus
matchThreadId bool
effAddrOffset Word
shouldClearReservation bool
}{
{name: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true},
{name: "matching reservation, diff thread", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: false, shouldClearReservation: true},
{name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
{name: "mismatched reservation", llReservationStatus: multithreaded.LLStatusActive64bit, matchThreadId: false, effAddrOffset: 8, shouldClearReservation: false},
{name: "no reservation, matching addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, shouldClearReservation: true},
{name: "no reservation, mismatched addr", llReservationStatus: multithreaded.LLStatusNone, matchThreadId: true, effAddrOffset: 8, shouldClearReservation: false},
}
for i, c := range cases {
for _, v := range llVariations {
tName := fmt.Sprintf("%v (%v)", c.name, v.name)
t.Run(tName, func(t *testing.T) {
t.Parallel()
effAddr := arch.AddressMask & c.addr
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageValue)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageValue)
goVm, state, contracts := setup(t, i, oracle)
step := state.GetStep()
// Define LL-related params
llAddress := effAddr + v.effAddrOffset
llOwnerThread := state.GetCurrentThread().ThreadId
if !v.matchThreadId {
llOwnerThread += 1
}
// Set up state
state.PreimageKey = preimageKey
state.PreimageOffset = c.preimageOffset
state.GetRegistersRef()[2] = arch.SysRead
state.GetRegistersRef()[4] = exec.FdPreimageRead
state.GetRegistersRef()[5] = c.addr
state.GetRegistersRef()[6] = c.count
testutil.StoreInstruction(state.GetMemory(), state.GetPC(), syscallInsn)
state.LLReservationStatus = v.llReservationStatus
state.LLAddress = llAddress
state.LLOwnerThread = llOwnerThread
state.GetMemory().SetWord(effAddr, c.prestateMem)
// Setup expectations
expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep()
expected.ActiveThread().Registers[2] = c.writeLen
expected.ActiveThread().Registers[7] = 0 // no error
expected.PreimageOffset += c.writeLen
expected.ExpectMemoryWordWrite(effAddr, c.postateMem)
if v.shouldClearReservation {
expected.LLReservationStatus = multithreaded.LLStatusNone
expected.LLAddress = 0
expected.LLOwnerThread = 0
}
if c.shouldPanic {
require.Panics(t, func() { _, _ = goVm.Step(true) })
testutil.AssertPreimageOracleReverts(t, preimageKey, preimageValue, c.preimageOffset, contracts)
} else {
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Check expectations
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
}
})
}
}
}
func testNoopSyscall(t *testing.T, syscalls map[string]uint32) {
for noopName, noopVal := range syscalls {
t.Run(noopName, func(t *testing.T) {
t.Parallel()
goVm, state, contracts := setup(t, int(noopVal), nil)
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = Word(noopVal) // Set syscall number
step := state.Step
// Set up post-state expectations
expected := mttestutil.NewExpectedMTState(state)
expected.ExpectStep()
expected.ActiveThread().Registers[2] = 0
expected.ActiveThread().Registers[7] = 0
// State transition
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
// Validate post-state
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
})
}
}
func testUnsupportedSyscall(t *testing.T, unsupportedSyscalls []uint32) {
for i, syscallNum := range unsupportedSyscalls {
testName := fmt.Sprintf("Unsupported syscallNum %v", syscallNum)
i := i
syscallNum := syscallNum
t.Run(testName, func(t *testing.T) {
t.Parallel()
goVm, state, contracts := setup(t, i*3434, nil)
// Setup basic getThreadId syscall instruction
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = Word(syscallNum)
proofData := multiThreadedProofGenerator(t, state)
// Set up post-state expectations
require.Panics(t, func() { _, _ = goVm.Step(true) })
errorMessage := "unimplemented syscall"
testutil.AssertEVMReverts(t, state, contracts, nil, proofData, testutil.CreateErrorStringMatcher(errorMessage))
})
}
}
// signExtend64 is used to sign-extend 32-bit words for 64-bit compatibility
func signExtend64(w Word) Word {
if arch.IsMips32 {
return w
} else {
return exec.SignExtend(w, 32)
}
}
const seed = 0xdead
var rand = testutil.NewRandHelper(seed)
// randomizeUpperWord is used to assert that 32-bit operations use the lower word only
func randomizeUpperWord(w Word) Word {
if arch.IsMips32 {
return w
} else {
if w>>32 == 0x0 { // nolint:staticcheck
rnd := rand.Uint32()
upper := uint64(rnd) << 32
return Word(upper | uint64(uint32(w)))
} else {
return w
}
}
}
......@@ -4,6 +4,7 @@ package testutil
import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
......@@ -46,3 +47,10 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui
func ToSignedInteger(x Word) arch.SignedInteger {
return arch.SignedInteger(x)
}
// Cannon32OnlyTest skips the test if it's not a cannon64 build
func Cannon32OnlyTest(t testing.TB, msg string, args ...any) {
if !arch.IsMips32 {
t.Skipf(msg, args...)
}
}
......@@ -141,10 +141,3 @@ func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.Create
require.Equal(t, expectedStdOut, stdOutBuf.String(), "stdout")
require.Equal(t, expectedStdErr, stdErrBuf.String(), "stderr")
}
func TemporarilySkip64BitTests(t *testing.T) {
if !arch.IsMips32 {
// TODO(#12598) Update and enable these tests
t.Skip("Skipping 64-bit test")
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment