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 ...@@ -18,6 +18,9 @@ endif
# The MIPS64 r1 opcodes not supported by cannon. This list does not include coprocess-specific opcodes. # The MIPS64 r1 opcodes not supported by cannon. This list does not include coprocess-specific opcodes.
UNSUPPORTED_OPCODES := (dclo|dclz) UNSUPPORTED_OPCODES := (dclo|dclz)
CANNON32_FUZZTIME := 10s
CANNON64_FUZZTIME := 20s
cannon32-impl: cannon32-impl:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build --tags=cannon32 -v $(LDFLAGS) -o ./bin/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: ...@@ -87,28 +90,28 @@ cannon-stf-verify:
fuzz: fuzz:
printf "%s\n" \ printf "%s\n" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallBrk ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallMmap ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallExitGroup ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFcntl32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallFcntl ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateHintRead ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateHintWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT32 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime $(CANNON32_FUZZTIME) -fuzz=FuzzStateSyscallCloneMT ./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 $(CANNON64_FUZZTIME) -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 $(CANNON64_FUZZTIME) -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 $(CANNON64_FUZZTIME) -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 $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallBrk ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallMmap ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallExitGroup ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallFcntl ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStateHintRead ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStatePreimageRead ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStateHintWrite ./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 $(CANNON64_FUZZTIME) -fuzz=FuzzStatePreimageWrite ./mipsevm/tests" \
"go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStateSyscallCloneMT64 ./mipsevm/tests" \ "go test $(FUZZLDFLAGS) --tags=cannon64 -run NOTAREALTEST -v -fuzztime $(CANNON64_FUZZTIME) -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests" \
| parallel -j 8 {} | parallel -j 8 {}
.PHONY: \ .PHONY: \
......
...@@ -455,10 +455,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem ...@@ -455,10 +455,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem
assertMips64(insn) assertMips64(insn)
return rt return rt
default: 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 { func SignExtend(dat Word, idx Word) Word {
......
...@@ -8,22 +8,12 @@ import ( ...@@ -8,22 +8,12 @@ import (
"os" "os"
"testing" "testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestEVMSingleStep_Operators64(t *testing.T) { func TestEVM_SingleStep_Operators64(t *testing.T) {
cases := []struct { cases := []operatorTestCase{
name string
isImm bool
rs Word
rt Word
imm uint16
opcode uint32
funct uint32
expectRes Word
}{
{name: "dadd. both unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // dadd t0, s1, s2 {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. 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 {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) { ...@@ -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", 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)}, {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) func TestEVM_SingleStep_Bitwise64(t *testing.T) {
for i, tt := range cases { cases := []operatorTestCase{
testName := fmt.Sprintf("%v %v", v.Name, tt.name) {name: "and", funct: 0x24, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(160)}, // and t0, s1, s2
t.Run(testName, func(t *testing.T) { {name: "andi", opcode: 0xc, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(0)}, // andi t0, s1, 40
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) {name: "or", funct: 0x25, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1530)}, // or t0, s1, s2
state := goVm.GetState() {name: "ori", opcode: 0xd, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // ori t0, s1, 40
var insn uint32 {name: "xor", funct: 0x26, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(1370)}, // xor t0, s1, s2
var rsReg uint32 = 17 {name: "xori", opcode: 0xe, isImm: true, rs: Word(4), rt: Word(1), imm: uint16(40), expectRes: Word(44)}, // xori t0, s1, 40
var rtReg uint32 {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
var rdReg uint32 {name: "slt", funct: 0x2a, isImm: false, rs: 0xFF_FF_FF_FE, rt: Word(5), expectRes: Word(0)}, // slt t0, s1, s2
if tt.isImm { {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
rtReg = 8 {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2
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)
})
} }
testOperators(t, cases, false)
} }
func TestEVMSingleStep_Shift64(t *testing.T) { func TestEVM_SingleStep_Shift64(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
rd Word rd Word
...@@ -218,17 +182,10 @@ func TestEVMSingleStep_Shift64(t *testing.T) { ...@@ -218,17 +182,10 @@ func TestEVMSingleStep_Shift64(t *testing.T) {
} }
} }
func TestEVMSingleStep_LoadStore64(t *testing.T) { func TestEVM_SingleStep_LoadStore64(t *testing.T) {
cases := []struct { t1 := Word(0xFF000000_00000108)
name string
rs Word cases := []loadStoreTestCase{
rt Word
opcode uint32
memVal Word
expectMemVal Word
expectRes Word
imm uint16
}{
{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 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 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) {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) { ...@@ -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", 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) {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)
} }
// use a fixed base for all tests
v := GetMultiThreadedTestCase(t) for i := range cases {
var t1 Word = 0xFF000000_00000108 cases[i].base = t1
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)
})
} }
testLoadStore(t, cases)
} }
func TestEVMSingleStep_DivMult64(t *testing.T) { func TestEVM_SingleStep_MulDiv64(t *testing.T) {
cases := []struct { cases := []mulDivTestCase{
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)},
// dmult s1, s2 // dmult s1, s2
// expected hi,lo were verified using qemu-mips // expected hi,lo were verified using qemu-mips
{name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, {name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0},
...@@ -514,9 +423,9 @@ func TestEVMSingleStep_DivMult64(t *testing.T) { ...@@ -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}, {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 // ddiv rs, rt
{name: "ddiv", funct: 0x1e, rs: 0, 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, expectPanic: "instruction divide 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, expectPanic: "instruction divide 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: 0, rt: 1, expectLo: 0, expectHi: 0},
{name: "ddiv", funct: 0x1e, rs: 1, rt: 1, expectLo: 1, 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}, {name: "ddiv", funct: 0x1e, rs: 10, rt: 3, expectLo: 3, expectHi: 1},
...@@ -527,9 +436,9 @@ func TestEVMSingleStep_DivMult64(t *testing.T) { ...@@ -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}, {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 // ddivu
{name: "ddivu", funct: 0x1f, rs: 0, 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, expectPanic: "instruction divide 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, expectPanic: "instruction divide 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: 0, rt: 1, expectLo: 0, expectHi: 0},
{name: "ddivu", funct: 0x1f, rs: 1, rt: 1, expectLo: 1, 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}, {name: "ddivu", funct: 0x1f, rs: 10, rt: 3, expectLo: 3, expectHi: 1},
...@@ -540,57 +449,16 @@ func TestEVMSingleStep_DivMult64(t *testing.T) { ...@@ -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}, {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 // 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: "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, expectPanic: "instruction divide 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) testMulDiv(t, cases, false)
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)
}
})
}
} }
func TestEVMSingleStep_Branch64(t *testing.T) { func TestEVM_SingleStep_Branch64(t *testing.T) {
versions := GetMipsVersionTestCases(t) t.Parallel()
cases := []struct { cases := []branchTestCase{
name string
pc Word
expectNextPC Word
opcode uint32
regimm uint32
expectLink bool
rs arch.SignedInteger
rt Word
offset uint16
}{
// blez // blez
{name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, {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}, {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) { ...@@ -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}, {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 { testBranch(t, cases)
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)
})
}
}
} }
...@@ -20,7 +20,9 @@ import ( ...@@ -20,7 +20,9 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "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") testFiles, err := os.ReadDir("open_mips_tests/test/bin")
require.NoError(t, err) require.NoError(t, err)
...@@ -36,7 +38,6 @@ func TestEVM(t *testing.T) { ...@@ -36,7 +38,6 @@ func TestEVM(t *testing.T) {
for _, f := range testFiles { for _, f := range testFiles {
testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name) testName := fmt.Sprintf("%v (%v)", f.Name(), c.Name)
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
for _, skipped := range skipped { for _, skipped := range skipped {
if f.Name() == skipped { if f.Name() == skipped {
t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name()) t.Skipf("Skipping explicitly excluded open_mips testcase: %v", f.Name())
...@@ -105,7 +106,7 @@ func TestEVM(t *testing.T) { ...@@ -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) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
...@@ -150,18 +151,8 @@ func TestEVMSingleStep_Jump(t *testing.T) { ...@@ -150,18 +151,8 @@ func TestEVMSingleStep_Jump(t *testing.T) {
} }
} }
func TestEVMSingleStep_Operators(t *testing.T) { func TestEVM_SingleStep_Operators(t *testing.T) {
versions := GetMipsVersionTestCases(t) cases := []operatorTestCase{
cases := []struct {
name string
isImm bool
rs Word
rt Word
imm uint16
funct uint32
opcode uint32
expectRes Word
}{
{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(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(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 {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) { ...@@ -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(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(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 {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: "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: "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: "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) { ...@@ -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: "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 {name: "sltu", funct: 0x2b, isImm: false, rs: Word(1200), rt: Word(490), expectRes: Word(0)}, // sltu t0, s1, s2
} }
testOperators(t, cases, false)
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)
})
}
}
} }
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) loadMemVal := Word(0x11_22_33_44)
loadMemValNeg := Word(0xF1_F2_F3_F4) loadMemValNeg := Word(0xF1_F2_F3_F4)
rtVal := Word(0xaa_bb_cc_dd) rtVal := Word(0xaa_bb_cc_dd)
versions := GetMipsVersionTestCases(t) cases := []loadStoreTestCase{
cases := []struct {
name string
rt Word
base Word
imm uint32
opcode uint32
memVal Word
expectMemVal Word
expectRes Word
}{
{name: "lb, offset=0", opcode: uint32(0x20), base: 0x100, imm: 0x20, memVal: loadMemVal, expectRes: 0x11}, {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=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}, {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) { ...@@ -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: "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}, {name: "swr unaligned", opcode: uint32(0x2e), base: 0x100, imm: 0x1, rt: rtVal, expectMemVal: 0xcc_dd_00_00},
} }
testLoadStore(t, cases)
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)
})
}
}
} }
func TestEVMSingleStep_MovzMovn(t *testing.T) { func TestEVM_SingleStep_MovzMovn(t *testing.T) {
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
...@@ -406,7 +310,7 @@ func TestEVMSingleStep_MovzMovn(t *testing.T) { ...@@ -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) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
...@@ -442,23 +346,8 @@ func TestEVMSingleStep_MfhiMflo(t *testing.T) { ...@@ -442,23 +346,8 @@ func TestEVMSingleStep_MfhiMflo(t *testing.T) {
} }
} }
func TestEVMSingleStep_MulDiv(t *testing.T) { func TestEVM_SingleStep_MulDiv(t *testing.T) {
var tracer *tracing.Hooks cases := []mulDivTestCase{
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
}{
{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(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(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 {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) { ...@@ -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(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_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_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", 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: "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", 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: "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 { testMulDiv(t, cases, true)
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)
})
}
}
} }
func TestEVMSingleStep_MthiMtlo(t *testing.T) { func TestEVM_SingleStep_MthiMtlo(t *testing.T) {
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
...@@ -631,7 +476,7 @@ func TestEVM_MMap(t *testing.T) { ...@@ -631,7 +476,7 @@ func TestEVM_MMap(t *testing.T) {
} }
} }
func TestEVMSysWriteHint(t *testing.T) { func TestEVM_SysWriteHint(t *testing.T) {
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
cases := []struct { cases := []struct {
name string name string
...@@ -824,7 +669,7 @@ func TestEVMSysWriteHint(t *testing.T) { ...@@ -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 var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer
versions := GetMipsVersionTestCases(t) versions := GetMipsVersionTestCases(t)
...@@ -846,7 +691,7 @@ func TestEVMFault(t *testing.T) { ...@@ -846,7 +691,7 @@ func TestEVMFault(t *testing.T) {
errMsg testutil.ErrMatcher errMsg testutil.ErrMatcher
memoryProofAddresses []Word 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: "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: "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()}, {name: "misaligned instruction", pc: 1, nextPC: 4, insn: 0b110111_00001_00001 << 16, errMsg: misAlignedInstructionErr()},
...@@ -859,7 +704,6 @@ func TestEVMFault(t *testing.T) { ...@@ -859,7 +704,6 @@ func TestEVMFault(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) testName := fmt.Sprintf("%v (%v)", tt.name, v.Name)
t.Run(testName, func(t *testing.T) { 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)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithPC(tt.pc), testutil.WithNextPC(tt.nextPC))
state := goVm.GetState() state := goVm.GetState()
testutil.StoreInstruction(state.GetMemory(), 0, tt.insn) testutil.StoreInstruction(state.GetMemory(), 0, tt.insn)
...@@ -874,7 +718,7 @@ func TestEVMFault(t *testing.T) { ...@@ -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" { if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
} }
...@@ -921,7 +765,7 @@ func TestHelloEVM(t *testing.T) { ...@@ -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" { if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
} }
...@@ -966,7 +810,7 @@ func TestClaimEVM(t *testing.T) { ...@@ -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" { if os.Getenv("SKIP_SLOW_TESTS") == "true" {
t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled") t.Skip("Skipping slow test because SKIP_SLOW_TESTS is enabled")
} }
...@@ -1010,19 +854,10 @@ func TestEntryEVM(t *testing.T) { ...@@ -1010,19 +854,10 @@ func TestEntryEVM(t *testing.T) {
} }
} }
func TestEVMSingleStepBranch(t *testing.T) { func TestEVM_SingleStep_Branch32(t *testing.T) {
versions := GetMipsVersionTestCases(t) testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_SingleStep_Branch64")
cases := []struct { t.Parallel()
name string cases := []branchTestCase{
pc Word
expectNextPC Word
opcode uint32
regimm uint32
expectLink bool
rs arch.SignedInteger
rt Word
offset uint16
}{
// blez // blez
{name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, {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}, {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) { ...@@ -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}, {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 { testBranch(t, cases)
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)
})
}
}
} }
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
package tests package tests
import ( import (
"encoding/binary"
"fmt" "fmt"
"slices"
"testing" "testing"
"github.com/stretchr/testify/require" "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/arch"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
...@@ -360,3 +363,122 @@ func TestEVM_MT64_SCD(t *testing.T) { ...@@ -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 ( ...@@ -9,7 +9,6 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
...@@ -19,7 +18,6 @@ import ( ...@@ -19,7 +18,6 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil" mttestutil "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded/testutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
) )
type Word = arch.Word type Word = arch.Word
...@@ -190,37 +188,14 @@ func TestEVM_MT_SC(t *testing.T) { ...@@ -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 := make([]byte, 0, 8)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x12_34_56_78)
preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32) preimageValue = binary.BigEndian.AppendUint32(preimageValue, 0x98_76_54_32)
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},
}
cases := []struct {
name string
addr Word
count Word
writeLen Word
preimageOffset Word
prestateMem Word
postateMem Word
shouldPanic bool
}{
{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 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 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}, {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) { ...@@ -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 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}, {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 testMTSysReadPreimage(t, preimageValue, cases)
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 TestEVM_MT_StoreOpsClearMemReservation(t *testing.T) { func TestEVM_MT_StoreOpsClearMemReservation32(t *testing.T) {
llVariations := []struct { t.Parallel()
name string testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_MT_StoreOpsClearMemReservation64")
llReservationStatus multithreaded.LLReservationStatus
matchThreadId bool cases := []testMTStoreOpsClearMemReservationTestCase{
effAddrOffset Word {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},
shouldClearReservation bool {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: "matching reservation", llReservationStatus: multithreaded.LLStatusActive32bit, matchThreadId: true, shouldClearReservation: true}, {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: "matching reservation, unaligned", llReservationStatus: multithreaded.LLStatusActive32bit, effAddrOffset: 1, matchThreadId: true, shouldClearReservation: true}, {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},
{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)
})
}
} }
testMTStoreOpsClearMemReservation(t, cases)
} }
func TestEVM_SysClone_FlagHandling(t *testing.T) { func TestEVM_SysClone_FlagHandling(t *testing.T) {
...@@ -633,39 +483,38 @@ func TestEVM_PopExitedThread(t *testing.T) { ...@@ -633,39 +483,38 @@ func TestEVM_PopExitedThread(t *testing.T) {
} }
func TestEVM_SysFutex_WaitPrivate(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 { cases := []struct {
name string name string
addressParam Word addressParam uint64
effAddr Word effAddr uint64
targetValue Word targetValue uint64
actualValue Word actualValue uint64
timeout Word timeout uint64
shouldFail bool shouldFail bool
shouldSetTimeout bool shouldSetTimeout bool
}{ }{
{name: "successful wait, no timeout", addressParam: 0x1234, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01}, {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: 0x1235, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01}, {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: 0x1200, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, shouldFail: true}, {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: 0x1203, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, 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: 0x1234, effAddr: 0x1234, targetValue: 0x01, actualValue: 0x01, timeout: 1000000, shouldSetTimeout: 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: 0x1232, effAddr: 0x1230, targetValue: 0x01, actualValue: 0x01, 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: 0x1200, effAddr: 0x1200, targetValue: 0x01, actualValue: 0x02, timeout: 2000000, shouldFail: 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: 0x120F, effAddr: 0x120C, targetValue: 0x01, actualValue: 0x02, 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 { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, i*1234, nil) goVm, state, contracts := setup(t, i*1234, nil)
step := state.GetStep() step := state.GetStep()
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) 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()[2] = arch.SysFutex // Set syscall number
state.GetRegistersRef()[4] = c.addressParam state.GetRegistersRef()[4] = Word(c.addressParam)
state.GetRegistersRef()[5] = exec.FutexWaitPrivate state.GetRegistersRef()[5] = exec.FutexWaitPrivate
state.GetRegistersRef()[6] = c.targetValue state.GetRegistersRef()[6] = Word(c.targetValue)
state.GetRegistersRef()[7] = c.timeout state.GetRegistersRef()[7] = Word(c.timeout)
// Setup expectations // Setup expectations
expected := mttestutil.NewExpectedMTState(state) expected := mttestutil.NewExpectedMTState(state)
...@@ -678,8 +527,8 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { ...@@ -678,8 +527,8 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
expected.ActiveThread().Registers[7] = exec.MipsEAGAIN expected.ActiveThread().Registers[7] = exec.MipsEAGAIN
} else { } else {
// PC and return registers should not update on success, updates happen when wait completes // PC and return registers should not update on success, updates happen when wait completes
expected.ActiveThread().FutexAddr = c.effAddr expected.ActiveThread().FutexAddr = Word(c.effAddr)
expected.ActiveThread().FutexVal = c.targetValue expected.ActiveThread().FutexVal = Word(c.targetValue)
expected.ActiveThread().FutexTimeoutStep = exec.FutexNoTimeout expected.ActiveThread().FutexTimeoutStep = exec.FutexNoTimeout
if c.shouldSetTimeout { if c.shouldSetTimeout {
expected.ActiveThread().FutexTimeoutStep = step + exec.FutexTimeoutSteps + 1 expected.ActiveThread().FutexTimeoutStep = step + exec.FutexTimeoutSteps + 1
...@@ -687,9 +536,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { ...@@ -687,9 +536,7 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
} }
// State transition // State transition
var err error stepWitness, err := goVm.Step(true)
var stepWitness *mipsevm.StepWitness
stepWitness, err = goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
// Validate post-state // Validate post-state
...@@ -700,39 +547,38 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) { ...@@ -700,39 +547,38 @@ func TestEVM_SysFutex_WaitPrivate(t *testing.T) {
} }
func TestEVM_SysFutex_WakePrivate(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 { cases := []struct {
name string name string
addressParam Word addressParam uint64
effAddr Word effAddr uint64
activeThreadCount int activeThreadCount int
inactiveThreadCount int inactiveThreadCount int
traverseRight bool traverseRight bool
expectTraverseRight bool expectTraverseRight bool
}{ }{
{name: "Traverse right", addressParam: 0x6700, effAddr: 0x6700, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: 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: 0x6789, effAddr: 0x6788, 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: 0x6784, effAddr: 0x6784, activeThreadCount: 2, inactiveThreadCount: 0, 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: 0x678E, effAddr: 0x678C, 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: 0x6788, effAddr: 0x6788, activeThreadCount: 1, 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: 0x6789, effAddr: 0x6788, 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: 0x6788, effAddr: 0x6788, activeThreadCount: 2, inactiveThreadCount: 1, traverseRight: false}, {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: 0x6789, effAddr: 0x6788, 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: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 1, traverseRight: false, expectTraverseRight: true}, {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: 0x6789, effAddr: 0x6788, 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: 0x6788, effAddr: 0x6788, activeThreadCount: 1, inactiveThreadCount: 0, 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: 0x6789, effAddr: 0x6788, 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 { for i, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm, state, contracts := setup(t, i*1122, nil) goVm, state, contracts := setup(t, i*1122, nil)
mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount) mttestutil.SetupThreads(int64(i*2244), state, c.traverseRight, c.activeThreadCount, c.inactiveThreadCount)
step := state.Step step := state.Step
testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn) testutil.StoreInstruction(state.Memory, state.GetPC(), syscallInsn)
state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number state.GetRegistersRef()[2] = arch.SysFutex // Set syscall number
state.GetRegistersRef()[4] = c.addressParam state.GetRegistersRef()[4] = Word(c.addressParam)
state.GetRegistersRef()[5] = exec.FutexWakePrivate state.GetRegistersRef()[5] = exec.FutexWakePrivate
// Set up post-state expectations // Set up post-state expectations
...@@ -740,7 +586,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { ...@@ -740,7 +586,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
expected.ExpectStep() expected.ExpectStep()
expected.ActiveThread().Registers[2] = 0 expected.ActiveThread().Registers[2] = 0
expected.ActiveThread().Registers[7] = 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.ExpectPreemption(state)
expected.TraverseRight = c.expectTraverseRight expected.TraverseRight = c.expectTraverseRight
if c.traverseRight != c.expectTraverseRight { if c.traverseRight != c.expectTraverseRight {
...@@ -750,9 +596,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { ...@@ -750,9 +596,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
} }
// State transition // State transition
var err error stepWitness, err := goVm.Step(true)
var stepWitness *mipsevm.StepWitness
stepWitness, err = goVm.Step(true)
require.NoError(t, err) require.NoError(t, err)
// Validate post-state // Validate post-state
...@@ -760,6 +604,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) { ...@@ -760,6 +604,7 @@ func TestEVM_SysFutex_WakePrivate(t *testing.T) {
testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts) testutil.ValidateEVM(t, stepWitness, step, goVm, multithreaded.GetStateHashFn(), contracts)
}) })
} }
} }
func TestEVM_SysFutex_UnsupportedOp(t *testing.T) { func TestEVM_SysFutex_UnsupportedOp(t *testing.T) {
...@@ -1103,70 +948,27 @@ var NoopSyscalls = map[string]uint32{ ...@@ -1103,70 +948,27 @@ var NoopSyscalls = map[string]uint32{
"SysTimerDelete": 4261, "SysTimerDelete": 4261,
} }
func TestEVM_NoopSyscall(t *testing.T) { func TestEVM_NoopSyscall32(t *testing.T) {
for noopName, noopVal := range NoopSyscalls { testutil.Cannon32OnlyTest(t, "These tests are fully covered for 64-bits in TestEVM_NoopSyscall64")
t.Run(noopName, func(t *testing.T) { testNoopSyscall(t, NoopSyscalls)
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_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() t.Parallel()
var tracer *tracing.Hooks
var NoopSyscallNums = maps.Values(NoopSyscalls) 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 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) unsupportedSyscalls := make([]uint32, 0, 400)
for i := 4000; i < 4400; i++ { for i := 4000; i < 4400; i++ {
candidate := uint32(i) candidate := uint32(i)
if slices.Contains(SupportedSyscalls, candidate) || slices.Contains(NoopSyscallNums, candidate) { if slices.Contains(supportedSyscalls, candidate) || slices.Contains(noopSyscallNums, candidate) {
continue continue
} }
unsupportedSyscalls = append(unsupportedSyscalls, candidate) unsupportedSyscalls = append(unsupportedSyscalls, candidate)
} }
for i, syscallNum := range unsupportedSyscalls { testUnsupportedSyscall(t, 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))
})
}
} }
func TestEVM_EmptyThreadStacks(t *testing.T) { func TestEVM_EmptyThreadStacks(t *testing.T) {
......
...@@ -19,20 +19,11 @@ import ( ...@@ -19,20 +19,11 @@ import (
const syscallInsn = uint32(0x00_00_00_0c) const syscallInsn = uint32(0x00_00_00_0c)
func FuzzStateSyscallBrk32(f *testing.F) { func FuzzStateSyscallBrk(f *testing.F) {
doFuzzStateSyscallBrk(f)
}
func FuzzStateSyscallBrk64(f *testing.F) {
doFuzzStateSyscallBrk(f)
}
func doFuzzStateSyscallBrk(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, seed int64) { f.Fuzz(func(t *testing.T, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
state.GetRegistersRef()[2] = arch.SysBrk state.GetRegistersRef()[2] = arch.SysBrk
...@@ -57,15 +48,7 @@ func doFuzzStateSyscallBrk(f *testing.F) { ...@@ -57,15 +48,7 @@ func doFuzzStateSyscallBrk(f *testing.F) {
}) })
} }
func FuzzStateSyscallMmap32(f *testing.F) { func FuzzStateSyscallMmap(f *testing.F) {
doFuzzStateSyscallMmap(f)
}
func FuzzStateSyscallMmap64(f *testing.F) {
doFuzzStateSyscallMmap(f)
}
func doFuzzStateSyscallMmap(f *testing.F) {
// Add special cases for large memory allocation // Add special cases for large memory allocation
f.Add(Word(0), Word(0x1000), Word(program.HEAP_END), int64(1)) 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)) f.Add(Word(0), Word(1<<31), Word(program.HEAP_START), int64(2))
...@@ -76,7 +59,6 @@ func doFuzzStateSyscallMmap(f *testing.F) { ...@@ -76,7 +59,6 @@ func doFuzzStateSyscallMmap(f *testing.F) {
f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) { f.Fuzz(func(t *testing.T, addr Word, siz Word, heap Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed), testutil.WithHeap(heap)) testutil.WithRandomization(seed), testutil.WithHeap(heap))
state := goVm.GetState() state := goVm.GetState()
...@@ -121,20 +103,11 @@ func doFuzzStateSyscallMmap(f *testing.F) { ...@@ -121,20 +103,11 @@ func doFuzzStateSyscallMmap(f *testing.F) {
}) })
} }
func FuzzStateSyscallExitGroup32(f *testing.F) { func FuzzStateSyscallExitGroup(f *testing.F) {
doFuzzStateSyscallExitGroup(f)
}
func FuzzStateSyscallExitGroup64(f *testing.F) {
doFuzzStateSyscallExitGroup(f)
}
func doFuzzStateSyscallExitGroup(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) { f.Fuzz(func(t *testing.T, exitCode uint8, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed)) testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
...@@ -159,20 +132,11 @@ func doFuzzStateSyscallExitGroup(f *testing.F) { ...@@ -159,20 +132,11 @@ func doFuzzStateSyscallExitGroup(f *testing.F) {
}) })
} }
func FuzzStateSyscallFcntl32(f *testing.F) { func FuzzStateSyscallFcntl(f *testing.F) {
doFuzzStateSyscallFcntl(f)
}
func FuzzStateSyscallFcntl64(f *testing.F) {
doFuzzStateSyscallFcntl(f)
}
func doFuzzStateSyscallFcntl(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) { f.Fuzz(func(t *testing.T, fd Word, cmd Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(),
testutil.WithRandomization(seed)) testutil.WithRandomization(seed))
state := goVm.GetState() state := goVm.GetState()
...@@ -193,7 +157,7 @@ func doFuzzStateSyscallFcntl(f *testing.F) { ...@@ -193,7 +157,7 @@ func doFuzzStateSyscallFcntl(f *testing.F) {
expected.Registers[2] = 0 expected.Registers[2] = 0
expected.Registers[7] = 0 expected.Registers[7] = 0
default: default:
expected.Registers[2] = 0xFF_FF_FF_FF expected.Registers[2] = ^Word(0)
expected.Registers[7] = exec.MipsEBADF expected.Registers[7] = exec.MipsEBADF
} }
} else if cmd == 3 { } else if cmd == 3 {
...@@ -205,11 +169,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) { ...@@ -205,11 +169,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) {
expected.Registers[2] = 1 expected.Registers[2] = 1
expected.Registers[7] = 0 expected.Registers[7] = 0
default: default:
expected.Registers[2] = 0xFF_FF_FF_FF expected.Registers[2] = ^Word(0)
expected.Registers[7] = exec.MipsEBADF expected.Registers[7] = exec.MipsEBADF
} }
} else { } else {
expected.Registers[2] = 0xFF_FF_FF_FF expected.Registers[2] = ^Word(0)
expected.Registers[7] = exec.MipsEINVAL expected.Registers[7] = exec.MipsEINVAL
} }
...@@ -224,20 +188,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) { ...@@ -224,20 +188,11 @@ func doFuzzStateSyscallFcntl(f *testing.F) {
}) })
} }
func FuzzStateHintRead32(f *testing.F) { func FuzzStateHintRead(f *testing.F) {
doFuzzStateHintRead(f)
}
func FuzzStateHintRead64(f *testing.F) {
doFuzzStateHintRead(f)
}
func doFuzzStateHintRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
preimageData := []byte("hello world") preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData) // only used for hinting oracle := testutil.StaticOracle(t, preimageData) // only used for hinting
...@@ -270,15 +225,7 @@ func doFuzzStateHintRead(f *testing.F) { ...@@ -270,15 +225,7 @@ func doFuzzStateHintRead(f *testing.F) {
}) })
} }
func FuzzStatePreimageRead32(f *testing.F) { func FuzzStatePreimageRead(f *testing.F) {
doFuzzStatePreimageRead(f)
}
func FuzzStatePreimageRead64(f *testing.F) {
doFuzzStatePreimageRead(f)
}
func doFuzzStatePreimageRead(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, pc arch.Word, count arch.Word, preimageOffset arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
...@@ -342,20 +289,11 @@ func doFuzzStatePreimageRead(f *testing.F) { ...@@ -342,20 +289,11 @@ func doFuzzStatePreimageRead(f *testing.F) {
}) })
} }
func FuzzStateHintWrite32(f *testing.F) { func FuzzStateHintWrite(f *testing.F) {
doFuzzStateHintWrite(f)
}
func FuzzStateHintWrite64(f *testing.F) {
doFuzzStateHintWrite(f)
}
func doFuzzStateHintWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) { f.Fuzz(func(t *testing.T, addr Word, count Word, hint1, hint2, hint3 []byte, randSeed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
// Make sure pc does not overlap with hint data in memory // Make sure pc does not overlap with hint data in memory
pc := Word(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
...@@ -433,27 +371,18 @@ func doFuzzStateHintWrite(f *testing.F) { ...@@ -433,27 +371,18 @@ func doFuzzStateHintWrite(f *testing.F) {
}) })
} }
func FuzzStatePreimageWrite32(f *testing.F) { func FuzzStatePreimageWrite(f *testing.F) {
doFuzzStatePreimageWrite(f)
}
func FuzzStatePreimageWrite64(f *testing.F) {
doFuzzStatePreimageWrite(f)
}
func doFuzzStatePreimageWrite(f *testing.F) {
versions := GetMipsVersionTestCases(f) versions := GetMipsVersionTestCases(f)
f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) { f.Fuzz(func(t *testing.T, addr arch.Word, count arch.Word, seed int64) {
for _, v := range versions { for _, v := range versions {
t.Run(v.Name, func(t *testing.T) { t.Run(v.Name, func(t *testing.T) {
testutil.TemporarilySkip64BitTests(t)
// Make sure pc does not overlap with preimage data in memory // Make sure pc does not overlap with preimage data in memory
pc := Word(0) pc := Word(0)
if addr <= 8 { if addr <= 8 {
addr += 8 addr += 8
} }
effAddr := addr & arch.AddressMask 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") preimageData := []byte("hello world")
preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey() preimageKey := preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey()
oracle := testutil.StaticOracle(t, preimageData) oracle := testutil.StaticOracle(t, preimageData)
...@@ -471,7 +400,7 @@ func doFuzzStatePreimageWrite(f *testing.F) { ...@@ -471,7 +400,7 @@ func doFuzzStatePreimageWrite(f *testing.F) {
expectBytesWritten := count expectBytesWritten := count
alignment := addr & arch.ExtMask alignment := addr & arch.ExtMask
sz := 4 - alignment sz := arch.WordSizeBytes - alignment
if sz < expectBytesWritten { if sz < expectBytesWritten {
expectBytesWritten = sz expectBytesWritten = sz
} }
......
...@@ -13,18 +13,9 @@ import ( ...@@ -13,18 +13,9 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
func FuzzStateSyscallCloneMT32(f *testing.F) { func FuzzStateSyscallCloneMT(f *testing.F) {
doFuzzStateSyscallCloneMT(f)
}
func FuzzStateSyscallCloneMT64(f *testing.F) {
doFuzzStateSyscallCloneMT(f)
}
func doFuzzStateSyscallCloneMT(f *testing.F) {
v := GetMultiThreadedTestCase(f) v := GetMultiThreadedTestCase(f)
f.Fuzz(func(t *testing.T, nextThreadId, stackPtr Word, seed int64) { 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)) goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(seed))
state := mttestutil.GetMtState(t, goVm) state := mttestutil.GetMtState(t, goVm)
// Update existing threads to avoid collision with nextThreadId // 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 ...@@ -4,6 +4,7 @@ package testutil
import ( import (
"bytes" "bytes"
"testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -46,3 +47,10 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui ...@@ -46,3 +47,10 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui
func ToSignedInteger(x Word) arch.SignedInteger { func ToSignedInteger(x Word) arch.SignedInteger {
return arch.SignedInteger(x) 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 ...@@ -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, expectedStdOut, stdOutBuf.String(), "stdout")
require.Equal(t, expectedStdErr, stdErrBuf.String(), "stderr") 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