From ad0ec1ea859b42e97ad52ae150b663d2e5a86825 Mon Sep 17 00:00:00 2001 From: Inphi Date: Wed, 23 Oct 2024 15:17:05 -0700 Subject: [PATCH] cannon: Finish emulating rest of 64-bit instructions (#12483) * cannon: Finish emulating rest of 64-bit instructions This fixes the 64-bit stubs for various instructions (except lld/scd). * review comments; fix dmult * add todo * test div by zero * add a couple more dmultu tests * remove dead code * cannon: Fix remaining mips64 emulation bugs * fix 64-bit Makefile build script; review comments * fix build script --- cannon/Makefile | 4 +- cannon/mipsevm/README.md | 1 + cannon/mipsevm/arch/arch32.go | 2 + cannon/mipsevm/arch/arch64.go | 12 +- cannon/mipsevm/exec/mips_instructions.go | 177 +++-- .../multithreaded/instrumented_test.go | 9 +- cannon/mipsevm/multithreaded/mips.go | 8 +- .../singlethreaded/instrumented_test.go | 3 +- cannon/mipsevm/tests/evm_common64_test.go | 658 ++++++++++++++++++ cannon/mipsevm/tests/evm_common_test.go | 91 +++ .../mipsevm/tests/evm_multithreaded_test.go | 4 +- cannon/mipsevm/testutil/arch.go | 6 + cannon/mipsevm/testutil/elf.go | 10 + cannon/mipsevm/testutil/oracle.go | 2 +- cannon/mipsevm/testutil/vmtests.go | 21 +- cannon/testdata/example/Makefile | 11 +- cannon/testdata/example/alloc/go.mod | 2 +- cannon/testdata/example/claim/go.mod | 2 +- cannon/testdata/example/hello/go.mod | 4 +- go.mod | 13 +- go.sum | 36 +- packages/contracts-bedrock/semver-lock.json | 8 +- .../contracts-bedrock/src/cannon/MIPS.sol | 4 +- .../contracts-bedrock/src/cannon/MIPS2.sol | 8 +- .../src/cannon/libraries/MIPSInstructions.sol | 6 + .../src/cannon/libraries/MIPSSyscalls.sol | 2 + 26 files changed, 1009 insertions(+), 95 deletions(-) create mode 100644 cannon/mipsevm/tests/evm_common64_test.go diff --git a/cannon/Makefile b/cannon/Makefile index 5f74904011eca..ccf959fc9d4c8 100644 --- a/cannon/Makefile +++ b/cannon/Makefile @@ -43,7 +43,7 @@ elf: make -C ./testdata/example elf sanitize-program: - @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bgezal|bltzal)'; }; then \ + @if ! { mips-linux-gnu-objdump -d -j .text $$GUEST_PROGRAM | awk '{print $3}' | grep -Ew -m1 '(bltzal)'; }; then \ echo "guest program is sanitized for unsupported instructions"; \ else \ echo "found unsupported instructions in the guest program"; \ @@ -93,6 +93,8 @@ fuzz: go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneST ./mipsevm/tests # Multi-threaded tests go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallCloneMT ./mipsevm/tests + # 64-bit tests + go test $(FUZZLDFLAGS) -tags=cannon64 -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateDmultInsn ./mipsevm/tests .PHONY: \ cannon32-impl \ diff --git a/cannon/mipsevm/README.md b/cannon/mipsevm/README.md index df7558187992b..03afec250a4a8 100644 --- a/cannon/mipsevm/README.md +++ b/cannon/mipsevm/README.md @@ -12,6 +12,7 @@ Supported 63 instructions: | `Conditional Branch` | `beq` | Branch on equal. | | `Conditional Branch` | `bgez` | Branch on greater than or equal to zero. | | `Conditional Branch` | `bgtz` | Branch on greater than zero. | +| `Conditional Branch` | `bgezal` | Branch and link on greater than or equal to zero. | | `Conditional Branch` | `blez` | Branch on less than or equal to zero. | | `Conditional Branch` | `bltz` | Branch on less than zero. | | `Conditional Branch` | `bne` | Branch on not equal. | diff --git a/cannon/mipsevm/arch/arch32.go b/cannon/mipsevm/arch/arch32.go index 87cad3cf504d2..dbe9ee193d7a2 100644 --- a/cannon/mipsevm/arch/arch32.go +++ b/cannon/mipsevm/arch/arch32.go @@ -79,6 +79,8 @@ const ( SysLlseek = 4140 SysMinCore = 4217 SysTgkill = 4266 + SysGetRLimit = 4076 + SysLseek = 4019 // Profiling-related syscalls SysSetITimer = 4104 SysTimerCreate = 4257 diff --git a/cannon/mipsevm/arch/arch64.go b/cannon/mipsevm/arch/arch64.go index a9b7df70c5830..b260ebb1bbb33 100644 --- a/cannon/mipsevm/arch/arch64.go +++ b/cannon/mipsevm/arch/arch64.go @@ -25,10 +25,12 @@ const ( AddressMask = 0xFFFFFFFFFFFFFFF8 ExtMask = 0x7 - HeapStart = 0x10_00_00_00_00_00_00_00 - HeapEnd = 0x60_00_00_00_00_00_00_00 - ProgramBreak = 0x40_00_00_00_00_00_00_00 - HighMemoryStart = 0x7F_FF_FF_FF_D0_00_00_00 + // Ensure virtual address is limited to 48-bits as many user programs assume such to implement packed pointers + // limit 0x00_00_FF_FF_FF_FF_FF_FF + HeapStart = 0x00_00_10_00_00_00_00_00 + HeapEnd = 0x00_00_60_00_00_00_00_00 + ProgramBreak = 0x00_00_40_00_00_00_00_00 + HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00 ) // MIPS64 syscall table - https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/arch/mips/kernel/syscalls/syscall_n64.tbl. Generate the syscall numbers using the Makefile in that directory. @@ -85,6 +87,8 @@ const ( SysLlseek = UndefinedSysNr SysMinCore = 5026 SysTgkill = 5225 + SysGetRLimit = 5095 + SysLseek = 5008 // Profiling-related syscalls SysSetITimer = 5036 SysTimerCreate = 5216 diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index 1bf3849c4e3f9..b99f519fe2f98 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -2,13 +2,11 @@ package exec import ( "fmt" + "math/bits" "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" - - // TODO(#12205): MIPS64 port. Replace with a custom library - u128 "lukechampine.com/uint128" ) const ( @@ -16,6 +14,11 @@ const ( OpStoreConditional = 0x38 OpLoadLinked64 = 0x34 OpStoreConditional64 = 0x3c + OpLoadDoubleLeft = 0x1A + OpLoadDoubleRight = 0x1B + + // Return address register + RegRA = 31 ) func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun uint32) { @@ -31,7 +34,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory if opcode == 2 || opcode == 3 { linkReg := Word(0) if opcode == 3 { - linkReg = 31 + linkReg = RegRA } // Take the top bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset target := (cpu.NextPC & SignExtend(0xF0000000, 32)) | Word((insn&0x03FFFFFF)<<2) @@ -77,7 +80,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory } if (opcode >= 4 && opcode < 8) || opcode == 1 { - err = HandleBranch(cpu, registers, opcode, insn, rtReg, rs) + err = HandleBranch(cpu, registers, opcode, insn, rtReg, rs, stackTracker) return } @@ -85,7 +88,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory // memory fetch (all I-type) // we do the load for stores also mem := Word(0) - if opcode >= 0x20 { + if opcode >= 0x20 || opcode == OpLoadDoubleLeft || opcode == OpLoadDoubleRight { // M[R[rs]+SignExtImm] rs += SignExtendImmediate(insn) addr := rs & arch.AddressMask @@ -311,10 +314,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem case 0x3C: // dsll32 assertMips64(insn) return rt << (((insn >> 6) & 0x1f) + 32) - case 0x3E: // dsll32 + case 0x3E: // dsrl32 assertMips64(insn) return rt >> (((insn >> 6) & 0x1f) + 32) - case 0x3F: // dsll32 + case 0x3F: // dsra32 assertMips64(insn) return Word(int64(rt) >> (((insn >> 6) & 0x1f) + 32)) default: @@ -347,13 +350,25 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem mask := Word(arch.ExtMask - 1) return SignExtend((mem>>(msb-uint32(rs&mask)*8))&0xFFFF, 16) case 0x22: // lwl - val := mem << ((rs & 3) * 8) - mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) - return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + if arch.IsMips32 { + val := mem << ((rs & 3) * 8) + mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) + return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + } else { + // similar to the above mips32 implementation but loads are constrained to the nearest 4-byte memory word + shift := 32 - ((rs & 0x4) << 3) + w := uint32(mem >> shift) + val := uint64(w << ((rs & 3) * 8)) + mask := Word(uint32(0xFFFFFFFF) << ((rs & 3) * 8)) + return SignExtend(((rt & ^mask)|Word(val))&0xFFFFFFFF, 32) + } case 0x23: // lw - // TODO(#12205): port to MIPS64 - return mem - //return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) + if arch.IsMips32 { + return mem + } else { + // TODO(#12562): Simplify using LoadSubWord + return SignExtend((mem>>(32-((rs&0x4)<<3)))&0xFFFFFFFF, 32) + } case 0x24: // lbu msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit return (mem >> (msb - uint32(rs&arch.ExtMask)*8)) & 0xFF @@ -362,9 +377,25 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem mask := Word(arch.ExtMask - 1) return (mem >> (msb - uint32(rs&mask)*8)) & 0xFFFF case 0x26: // lwr - val := mem >> (24 - (rs&3)*8) - mask := Word(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)) - return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + if arch.IsMips32 { + val := mem >> (24 - (rs&3)*8) + mask := Word(uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)) + return SignExtend(((rt & ^mask)|val)&0xFFFFFFFF, 32) + } else { + // similar to the above mips32 implementation but constrained to the nearest 4-byte memory word + shift := 32 - ((rs & 0x4) << 3) + w := uint32(mem >> shift) + val := w >> (24 - (rs&3)*8) + mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8) + lwrResult := ((uint32(rt) & ^mask) | val) & 0xFFFFFFFF + if rs&3 == 3 { // loaded bit 31 + return SignExtend(Word(lwrResult), 32) + } else { + // NOTE: cannon64 implementation specific: We leave the upper word untouched + rtMask := uint64(0xFF_FF_FF_FF_00_00_00_00) + return (rt & Word(rtMask)) | Word(lwrResult) + } + } case 0x28: // sb msb := uint32(arch.WordSize - 8) // 24 for 32-bit and 56 for 64-bit val := (rt & 0xFF) << (msb - uint32(rs&arch.ExtMask)*8) @@ -378,18 +409,45 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem mask := ^Word(0) ^ Word(0xFFFF<> ((rs & 3) * 8) - mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8) - return (mem & Word(^mask)) | val + if arch.IsMips32 { + val := rt >> ((rs & 3) * 8) + mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8) + return (mem & Word(^mask)) | val + } else { + sr := (rs & 3) << 3 + val := ((rt & 0xFFFFFFFF) >> sr) << (32 - ((rs & 0x4) << 3)) + mask := (uint64(0xFFFFFFFF) >> sr) << (32 - ((rs & 0x4) << 3)) + return (mem & Word(^mask)) | val + } case 0x2b: // sw - // TODO(#12205): port to MIPS64 - return rt + if arch.IsMips32 { + return rt + } else { + sl := 32 - ((rs & 0x4) << 3) + val := (rt & 0xFFFFFFFF) << sl + mask := Word(0xFFFFFFFFFFFFFFFF ^ uint64(0xFFFFFFFF<> shift) + val := rt << (24 - (rs&3)*8) + mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8) + swrResult := (w & ^mask) | uint32(val) + // merge with the untouched bytes in mem + if shift > 0 { + return (Word(swrResult) << 32) | (mem & Word(^uint32(0))) // nolint: staticcheck + } else { + memMask := uint64(0xFF_FF_FF_FF_00_00_00_00) + return (mem & Word(memMask)) | Word(swrResult & ^uint32(0)) + } + } // MIPS64 case 0x1A: // ldl @@ -424,10 +482,7 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return mem case 0x3F: // sd assertMips64(insn) - sl := (rs & 0x7) << 3 - val := rt << sl - mask := ^Word(0) << sl - return (mem & ^mask) | val + return rt default: panic("invalid instruction") } @@ -446,12 +501,13 @@ func SignExtend(dat Word, idx Word) Word { } } -func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, insn uint32, rtReg Word, rs Word) error { +func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, insn uint32, rtReg Word, rs Word, stackTracker StackTracker) error { if cpu.NextPC != cpu.PC+4 { panic("branch in delay slot") } shouldBranch := false + linked := false if opcode == 4 || opcode == 5 { // beq/bne rt := registers[rtReg] shouldBranch = (rs == rt && opcode == 4) || (rs != rt && opcode == 5) @@ -463,10 +519,15 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, i // regimm rtv := (insn >> 16) & 0x1F if rtv == 0 { // bltz - shouldBranch = int32(rs) < 0 + shouldBranch = arch.SignedInteger(rs) < 0 } if rtv == 1 { // bgez - shouldBranch = int32(rs) >= 0 + shouldBranch = arch.SignedInteger(rs) >= 0 + } + if rtv == 0x11 { // bgezal (i.e. bal mnemonic) + shouldBranch = arch.SignedInteger(rs) >= 0 + registers[RegRA] = cpu.PC + 8 // always set regardless of branch taken + linked = true } } @@ -474,12 +535,16 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, i cpu.PC = cpu.NextPC // execute the delay slot first if shouldBranch { cpu.NextPC = prevPC + 4 + (SignExtend(Word(insn&0xFFFF), 16) << 2) // then continue with the instruction the branch jumps to. + if linked { + stackTracker.PushStack(prevPC, cpu.NextPC) + } } else { cpu.NextPC = cpu.NextPC + 4 // branch not taken } return nil } +// HandleHiLo handles instructions that modify HI and LO registers. It also additionally handles doubleword variable shift operations func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Word, rt Word, storeReg Word) error { val := Word(0) switch fun { @@ -521,22 +586,44 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Wor assertMips64Fun(fun) val = Word(int64(rt) >> (rs & 0x3F)) case 0x1c: // dmult - // TODO(#12205): port to MIPS64. Is signed multiply needed for dmult assertMips64Fun(fun) - acc := u128.From64(uint64(rs)).Mul(u128.From64(uint64(rt))) - cpu.HI = Word(acc.Hi) - cpu.LO = Word(acc.Lo) + a := int64(rs) + b := int64(rt) + negative := (a < 0) != (b < 0) // set if operands have different signs + + // Handle special case for most negative value to avoid overflow in negation + absA := uint64(abs64(a)) + absB := uint64(abs64(b)) + + hi, lo := bits.Mul64(absA, absB) + if negative { + // Two's complement negation: flip all bits and add 1 + hi = ^hi + lo = ^lo + if lo == 0xFFFFFFFFFFFFFFFF { + hi++ + } + lo++ + } + cpu.HI = Word(hi) + cpu.LO = Word(lo) case 0x1d: // dmultu assertMips64Fun(fun) - acc := u128.From64(uint64(rs)).Mul(u128.From64(uint64(rt))) - cpu.HI = Word(acc.Hi) - cpu.LO = Word(acc.Lo) + hi, lo := bits.Mul64(uint64(rs), uint64(rt)) + cpu.HI = Word(hi) + cpu.LO = Word(lo) case 0x1e: // ddiv assertMips64Fun(fun) + if rt == 0 { + panic("instruction divide by zero") + } cpu.HI = Word(int64(rs) % int64(rt)) cpu.LO = Word(int64(rs) / int64(rt)) case 0x1f: // ddivu assertMips64Fun(fun) + if rt == 0 { + panic("instruction divide by zero") + } cpu.HI = rs % rt cpu.LO = rs / rt } @@ -619,3 +706,11 @@ func calculateSubWordMaskAndOffset(addr Word, byteLength Word) (dataMask, bitOff return dataMask, bitOffset, bitLength } + +// abs64 returns the absolute value +func abs64(x int64) int64 { + if x < 0 { + return -x + } + return x +} diff --git a/cannon/mipsevm/multithreaded/instrumented_test.go b/cannon/mipsevm/multithreaded/instrumented_test.go index dd4635668458b..ec079e50c8ce7 100644 --- a/cannon/mipsevm/multithreaded/instrumented_test.go +++ b/cannon/mipsevm/multithreaded/instrumented_test.go @@ -11,11 +11,12 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM { - return NewInstrumentedState(state, po, stdOut, stdErr, log, nil) +func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM { + return NewInstrumentedState(state, po, stdOut, stdErr, log, meta) } func TestInstrumentedState_OpenMips(t *testing.T) { @@ -35,7 +36,7 @@ func TestInstrumentedState_Claim(t *testing.T) { func TestInstrumentedState_MultithreadedProgram(t *testing.T) { t.Parallel() - state, _ := testutil.LoadELFProgram(t, "../../testdata/example/bin/multithreaded.elf", CreateInitialState, false) + state, _ := testutil.LoadELFProgram(t, testutil.ProgramPath("multithreaded"), CreateInitialState, false) oracle := testutil.StaticOracle(t, []byte{}) var stdOutBuf, stdErrBuf bytes.Buffer @@ -78,7 +79,7 @@ func TestInstrumentedState_Alloc(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() - state, meta := testutil.LoadELFProgram(t, "../../testdata/example/bin/alloc.elf", CreateInitialState, false) + state, meta := testutil.LoadELFProgram(t, testutil.ProgramPath("alloc"), CreateInitialState, false) oracle := testutil.AllocOracle(t, test.numAllocs, test.allocSize) us := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr, testutil.CreateLogger(), meta) diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index cddf9fd590f13..f371da2c40532 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -168,9 +168,9 @@ func (m *InstrumentedState) handleSyscall() error { m.memoryTracker.TrackMemAccess(effAddr) m.state.Memory.SetWord(effAddr, secs) m.handleMemoryUpdate(effAddr) - m.memoryTracker.TrackMemAccess2(effAddr + 4) - m.state.Memory.SetWord(effAddr+4, nsecs) - m.handleMemoryUpdate(effAddr + 4) + m.memoryTracker.TrackMemAccess2(effAddr + arch.WordSizeBytes) + m.state.Memory.SetWord(effAddr+arch.WordSizeBytes, nsecs) + m.handleMemoryUpdate(effAddr + arch.WordSizeBytes) default: v0 = exec.SysErrorSignal v1 = exec.MipsEINVAL @@ -206,6 +206,8 @@ func (m *InstrumentedState) handleSyscall() error { case arch.SysTimerCreate: case arch.SysTimerSetTime: case arch.SysTimerDelete: + case arch.SysGetRLimit: + case arch.SysLseek: default: // These syscalls have the same values on 64-bit. So we use if-stmts here to avoid "duplicate case" compiler error for the cannon64 build if arch.IsMips32 && syscallNum == arch.SysFstat64 || syscallNum == arch.SysStat64 || syscallNum == arch.SysLlseek { diff --git a/cannon/mipsevm/singlethreaded/instrumented_test.go b/cannon/mipsevm/singlethreaded/instrumented_test.go index 8b4c61bd4e148..d4c190d9cd915 100644 --- a/cannon/mipsevm/singlethreaded/instrumented_test.go +++ b/cannon/mipsevm/singlethreaded/instrumented_test.go @@ -7,10 +7,11 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" ) -func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM { +func vmFactory(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM { return NewInstrumentedState(state, po, stdOut, stdErr, nil) } diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go new file mode 100644 index 0000000000000..70f59120abc05 --- /dev/null +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -0,0 +1,658 @@ +//go:build cannon64 +// +build cannon64 + +package tests + +import ( + "fmt" + "os" + "testing" + + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/stretchr/testify/require" +) + +func TestEVMSingleStep_Operators64(t *testing.T) { + cases := []struct { + name string + isImm bool + rs Word + rt Word + imm uint16 + opcode uint32 + funct uint32 + expectRes Word + }{ + {name: "dadd. both unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 32 and signed", funct: 0x2c, isImm: false, rs: Word(0x12), rt: Word(^uint32(0)), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 32", funct: 0x2c, isImm: false, rs: Word(^uint32(0)), rt: Word(0x12), expectRes: Word(0x1_00_00_00_11)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 64 and unsigned 32", funct: 0x2c, isImm: false, rs: Word(0x0FFFFFFF_00000012), rt: Word(0x20), expectRes: Word(0x0FFFFFFF_00000032)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 32 and signed", funct: 0x2c, isImm: false, rs: Word(12), rt: ^Word(0), expectRes: Word(11)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 32", funct: 0x2c, isImm: false, rs: ^Word(0), rt: Word(12), expectRes: Word(11)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 32. expect signed", funct: 0x2c, isImm: false, rs: ^Word(20), rt: Word(4), expectRes: ^Word(16)}, // dadd t0, s1, s2 + {name: "dadd. unsigned 32 and signed. expect signed", funct: 0x2c, isImm: false, rs: Word(4), rt: ^Word(20), expectRes: ^Word(16)}, // dadd t0, s1, s2 + {name: "dadd. both signed", funct: 0x2c, isImm: false, rs: ^Word(10), rt: ^Word(4), expectRes: ^Word(15)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 64. expect unsigned", funct: 0x2c, isImm: false, rs: ^Word(0), rt: Word(0x000000FF_00000000), expectRes: Word(0x000000FE_FFFFFFFF)}, // dadd t0, s1, s2 + {name: "dadd. signed and unsigned 64. expect signed", funct: 0x2c, isImm: false, rs: Word(0x80000000_00000000), rt: Word(0x40000000_00000000), expectRes: Word(0xC000000000000000)}, // dadd t0, s1, s2 + + {name: "daddu. both 32", funct: 0x2d, isImm: false, rs: Word(0x12), rt: Word(0x20), expectRes: Word(0x32)}, // daddu t0, s1, s2 + {name: "daddu. 32-bit. expect doubleword-sized", funct: 0x2d, isImm: false, rs: Word(0x12), rt: Word(^uint32(0)), expectRes: Word(0x1_00_00_00_11)}, // daddu t0, s1, s2 + {name: "daddu. 32-bit. expect double-word sized x", funct: 0x2d, isImm: false, rs: Word(^uint32(0)), rt: Word(0x12), expectRes: Word(0x1_00_00_00_11)}, // dadu t0, s1, s2 + {name: "daddu. doubleword-sized, word-sized", funct: 0x2d, isImm: false, rs: Word(0x0FFFFFFF_00000012), rt: Word(0x20), expectRes: Word(0x0FFFFFFF_00000032)}, // dadu t0, s1, s2 + {name: "daddu. overflow. rt sign bit set", funct: 0x2d, isImm: false, rs: Word(12), rt: ^Word(0), expectRes: Word(11)}, // dadu t0, s1, s2 + {name: "daddu. overflow. rs sign bit set", funct: 0x2d, isImm: false, rs: ^Word(0), rt: Word(12), expectRes: Word(11)}, // dadu t0, s1, s2 + {name: "daddu. doubleword-sized and word-sized", funct: 0x2d, isImm: false, rs: ^Word(20), rt: Word(4), expectRes: ^Word(16)}, // dadu t0, s1, s2 + {name: "daddu. word-sized and doubleword-sized", funct: 0x2d, isImm: false, rs: Word(4), rt: ^Word(20), expectRes: ^Word(16)}, // dadu t0, s1, s2 + {name: "daddu. both doubleword-sized. expect overflow", funct: 0x2d, isImm: false, rs: ^Word(10), rt: ^Word(4), expectRes: ^Word(15)}, // dadu t0, s1, s2 + + {name: "daddi word-sized", opcode: 0x18, isImm: true, rs: Word(12), rt: ^Word(0), imm: uint16(20), expectRes: Word(32)}, // daddi t0, s1, s2 + {name: "daddi doubleword-sized", opcode: 0x18, isImm: true, rs: Word(0x00000010_00000000), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x00000010_00000020)}, // daddi t0, s1, s2 + {name: "daddi 32-bit sign", opcode: 0x18, isImm: true, rs: Word(0xFF_FF_FF_FF), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x01_00_00_00_1F)}, // daddi t0, s1, s2 + {name: "daddi double-word signed", opcode: 0x18, isImm: true, rs: ^Word(0), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x1F)}, // daddi t0, s1, s2 + {name: "daddi double-word signed. expect signed", opcode: 0x18, isImm: true, rs: ^Word(0x10), rt: ^Word(0), imm: uint16(0x1), expectRes: ^Word(0xF)}, // daddi t0, s1, s2 + + {name: "daddiu word-sized", opcode: 0x19, isImm: true, rs: Word(4), rt: ^Word(0), imm: uint16(40), expectRes: Word(44)}, // daddiu t0, s1, 40 + {name: "daddiu doubleword-sized", opcode: 0x19, isImm: true, rs: Word(0x00000010_00000000), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x00000010_00000020)}, // daddiu t0, s1, 40 + {name: "daddiu 32-bit sign", opcode: 0x19, isImm: true, rs: Word(0xFF_FF_FF_FF), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x01_00_00_00_1F)}, // daddiu t0, s1, 40 + {name: "daddiu overflow", opcode: 0x19, isImm: true, rs: ^Word(0), rt: ^Word(0), imm: uint16(0x20), expectRes: Word(0x1F)}, // daddiu t0, s1, s2 + + {name: "dsub. both unsigned 32", funct: 0x2e, isImm: false, rs: Word(0x12), rt: Word(0x1), expectRes: Word(0x11)}, // dsub t0, s1, s2 + {name: "dsub. signed and unsigned 32", funct: 0x2e, isImm: false, rs: ^Word(1), rt: Word(0x1), expectRes: Word(^uint64(2))}, // dsub t0, s1, s2 + {name: "dsub. signed and unsigned 64", funct: 0x2e, isImm: false, rs: ^Word(1), rt: Word(0x00AABBCC_00000000), expectRes: ^Word(0x00AABBCC_00000001)}, // dsub t0, s1, s2 + {name: "dsub. both signed. unsigned result", funct: 0x2e, isImm: false, rs: ^Word(1), rt: ^Word(2), expectRes: Word(1)}, // dsub t0, s1, s2 + {name: "dsub. both signed. signed result", funct: 0x2e, isImm: false, rs: ^Word(2), rt: ^Word(1), expectRes: ^Word(0)}, // dsub t0, s1, s2 + {name: "dsub. signed and zero", funct: 0x2e, isImm: false, rs: ^Word(0), rt: Word(0), expectRes: ^Word(0)}, // dsub t0, s1, s2 + + {name: "dsubu. both unsigned 32", funct: 0x2f, isImm: false, rs: Word(0x12), rt: Word(0x1), expectRes: Word(0x11)}, // dsubu t0, s1, s2 + {name: "dsubu. signed and unsigned 32", funct: 0x2f, isImm: false, rs: ^Word(1), rt: Word(0x1), expectRes: Word(^uint64(2))}, // dsubu t0, s1, s2 + {name: "dsubu. signed and unsigned 64", funct: 0x2f, isImm: false, rs: ^Word(1), rt: Word(0x00AABBCC_00000000), expectRes: ^Word(0x00AABBCC_00000001)}, // dsubu t0, s1, s2 + {name: "dsubu. both signed. unsigned result", funct: 0x2f, isImm: false, rs: ^Word(1), rt: ^Word(2), expectRes: Word(1)}, // dsubu t0, s1, s2 + {name: "dsubu. both signed. signed result", funct: 0x2f, isImm: false, rs: ^Word(2), rt: ^Word(1), expectRes: ^Word(0)}, // dsubu t0, s1, s2 + {name: "dsubu. signed and zero", funct: 0x2f, isImm: false, rs: ^Word(0), rt: Word(0), expectRes: ^Word(0)}, // dsubu t0, s1, s2 + {name: "dsubu. overflow", funct: 0x2f, isImm: false, rs: Word(0x80000000_00000000), rt: Word(0x7FFFFFFF_FFFFFFFF), expectRes: Word(0x00000000_00000001)}, // dsubu t0, s1, s2 + + // dsllv + {name: "dsllv", funct: 0x14, rt: Word(0x20), rs: Word(0), expectRes: Word(0x20)}, + {name: "dsllv", funct: 0x14, rt: Word(0x20), rs: Word(1), expectRes: Word(0x40)}, + {name: "dsllv sign", funct: 0x14, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(1), expectRes: Word(0x00_00_00_00_00_00_00_40)}, + {name: "dsllv max", funct: 0x14, rt: Word(0xFF_FF_FF_FF_FF_FF_FF_Fe), rs: Word(0x3f), expectRes: Word(0x0)}, + {name: "dsllv max almost clear", funct: 0x14, rt: Word(0x1), rs: Word(0x3f), expectRes: Word(0x80_00_00_00_00_00_00_00)}, + + // dsrlv t0, s1, s2 + {name: "dsrlv", funct: 0x16, rt: Word(0x20), rs: Word(0), expectRes: Word(0x20)}, + {name: "dsrlv", funct: 0x16, rt: Word(0x20), rs: Word(1), expectRes: Word(0x10)}, + {name: "dsrlv sign-extend", funct: 0x16, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(1), expectRes: Word(0x40_00_00_00_00_00_00_10)}, + {name: "dsrlv max", funct: 0x16, rt: Word(0x7F_FF_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x0)}, + {name: "dsrlv max sign-extend", funct: 0x16, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(0x3f), expectRes: Word(0x1)}, + + // dsrav t0, s1, s2 + {name: "dsrav", funct: 0x17, rt: Word(0x20), rs: Word(0), expectRes: Word(0x20)}, + {name: "dsrav", funct: 0x17, rt: Word(0x20), rs: Word(1), expectRes: Word(0x10)}, + {name: "dsrav sign-extend", funct: 0x17, rt: Word(0x80_00_00_00_00_00_00_20), rs: Word(1), expectRes: Word(0xc0_00_00_00_00_00_00_10)}, + {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)}, + } + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + var insn uint32 + var rsReg uint32 = 17 + var rtReg uint32 + var rdReg uint32 + if tt.isImm { + rtReg = 8 + insn = tt.opcode<<26 | rsReg<<21 | rtReg<<16 | uint32(tt.imm) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[rsReg] = tt.rs + } else { + rtReg = 18 + rdReg = 8 + insn = rsReg<<21 | rtReg<<16 | rdReg<<11 | tt.funct + state.GetRegistersRef()[rsReg] = tt.rs + state.GetRegistersRef()[rtReg] = tt.rt + } + state.GetMemory().SetUint32(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, nil) + }) + } +} + +func TestEVMSingleStep_Shift(t *testing.T) { + cases := []struct { + name string + rd Word + rt Word + sa uint32 + funct uint32 + expectRes Word + }{ + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1)}, // dsll t8, s2, 0 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x2)}, // dsll t8, s2, 1 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 31, expectRes: Word(0x80_00_00_00)}, // dsll t8, s2, 31 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 1, expectRes: Word(0xFF_FF_FF_FE_00_00_00_00)}, // dsll t8, s2, 1 + {name: "dsll", funct: 0x38, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 31, expectRes: Word(0x80_00_00_00_00_00_00_00)}, // dsll t8, s2, 31 + + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1)}, // dsrl t8, s2, 0 + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x0)}, // dsrl t8, s2, 1 + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 1, expectRes: Word(0x7F_FF_FF_FF_80_00_00_00)}, // dsrl t8, s2, 1 + {name: "dsrl", funct: 0x3a, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 31, expectRes: Word(0x01_FF_FF_FF_FE)}, // dsrl t8, s2, 31 + + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1)}, // dsra t8, s2, 0 + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x0)}, // dsra t8, s2, 1 + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 1, expectRes: Word(0xFF_FF_FF_FF_80_00_00_00)}, // dsra t8, s2, 1 + {name: "dsra", funct: 0x3b, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_00_00_00_00), sa: 31, expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FE)}, // dsra t8, s2, 31 + + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x1_00_00_00_00)}, // dsll32 t8, s2, 0 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x2_00_00_00_00)}, // dsll32 t8, s2, 1 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 31, expectRes: Word(0x80_00_00_00_00_00_00_00)}, // dsll32 t8, s2, 31 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 1, expectRes: Word(0xFF_FF_FF_FE_00_00_00_00)}, // dsll32 t8, s2, 1 + {name: "dsll32", funct: 0x3c, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 31, expectRes: Word(0x80_00_00_00_00_00_00_00)}, // dsll32 t8, s2, 31 + + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x0)}, // dsrl32 t8, s2, 0 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 31, expectRes: Word(0x0)}, // dsrl32 t8, s2, 31 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 1, expectRes: Word(0x7F_FF_FF_FF)}, // dsrl32 t8, s2, 1 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 31, expectRes: Word(0x1)}, // dsrl32 t8, s2, 31 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1_0000_0000), sa: 0, expectRes: Word(0x1)}, // dsrl32 t8, s2, 0 + {name: "dsrl32", funct: 0x3e, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1_0000_0000), sa: 31, expectRes: Word(0x0)}, // dsrl32 t8, s2, 31 + + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 0, expectRes: Word(0x0)}, // dsra32 t8, s2, 0 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x1), sa: 1, expectRes: Word(0x0)}, // dsra32 t8, s2, 1 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF), sa: 0, expectRes: Word(0x0)}, // dsra32 t8, s2, 0 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x01_FF_FF_FF_FF), sa: 0, expectRes: Word(0x1)}, // dsra32 t8, s2, 0 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), sa: 1, expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_FF)}, // dsra32 t8, s2, 1 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0xFF_FF_FF_00_00_00_00_00), sa: 1, expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_80)}, // dsra32 t8, s2, 1 + {name: "dsra32", funct: 0x3f, rd: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), rt: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), sa: 31, expectRes: Word(0x0)}, // dsra32 t8, s2, 1 + } + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + var insn uint32 + var rtReg uint32 + var rdReg uint32 + rtReg = 18 + rdReg = 8 + insn = rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct + state.GetRegistersRef()[rdReg] = tt.rd + state.GetRegistersRef()[rtReg] = tt.rt + state.GetMemory().SetUint32(0, insn) + step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + 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, nil) + }) + } +} + +func TestEVMSingleStep_LoadStore64(t *testing.T) { + cases := []struct { + name string + rs Word + 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 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 3", opcode: uint32(0x20), imm: 3, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x74)}, // lb $t0, 3($t1) + {name: "lb 4", opcode: uint32(0x20), imm: 4, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x75)}, // lb $t0, 4($t1) + {name: "lb 5", opcode: uint32(0x20), imm: 5, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x76)}, // lb $t0, 5($t1) + {name: "lb 6", opcode: uint32(0x20), imm: 6, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x77)}, // lb $t0, 6($t1) + {name: "lb 7", opcode: uint32(0x20), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x78)}, // lb $t0, 7($t1) + {name: "lb sign-extended 0", opcode: uint32(0x20), memVal: Word(0x81_72_73_74_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_81)}, // lb $t0, 0($t1) + {name: "lb sign-extended 1", opcode: uint32(0x20), imm: 1, memVal: Word(0x71_82_73_74_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_82)}, // lb $t0, 1($t1) + {name: "lb sign-extended 2", opcode: uint32(0x20), imm: 2, memVal: Word(0x71_72_83_74_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_83)}, // lb $t0, 2($t1) + {name: "lb sign-extended 3", opcode: uint32(0x20), imm: 3, memVal: Word(0x71_72_73_84_75_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_84)}, // lb $t0, 3($t1) + {name: "lb sign-extended 4", opcode: uint32(0x20), imm: 4, memVal: Word(0x71_72_73_74_85_76_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_85)}, // lb $t0, 4($t1) + {name: "lb sign-extended 5", opcode: uint32(0x20), imm: 5, memVal: Word(0x71_72_73_74_75_86_77_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_86)}, // lb $t0, 5($t1) + {name: "lb sign-extended 6", opcode: uint32(0x20), imm: 6, memVal: Word(0x71_72_73_74_75_76_87_78), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_87)}, // lb $t0, 6($t1) + {name: "lb sign-extended 7", opcode: uint32(0x20), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_88), expectRes: Word(0xFF_FF_FF_FF_FF_FF_FF_88)}, // lb $t0, 7($t1) + + {name: "lh offset=0", opcode: uint32(0x21), memVal: Word(0x11223344_55667788), expectRes: Word(0x11_22)}, // lhu $t0, 0($t1) + {name: "lh offset=0 sign-extended", opcode: uint32(0x21), memVal: Word(0x81223344_55667788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_81_22)}, // lhu $t0, 0($t1) + {name: "lh offset=2", opcode: uint32(0x21), imm: 2, memVal: Word(0x11223344_55667788), expectRes: Word(0x33_44)}, // lhu $t0, 2($t1) + {name: "lh offset=2 sign-extended", opcode: uint32(0x21), imm: 2, memVal: Word(0x11228344_55667788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_83_44)}, // lhu $t0, 2($t1) + {name: "lh offset=4", opcode: uint32(0x21), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55_66)}, // lhu $t0, 4($t1) + {name: "lh offset=4 sign-extended", opcode: uint32(0x21), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_85_66)}, // lhu $t0, 4($t1) + {name: "lh offset=6", opcode: uint32(0x21), imm: 6, memVal: Word(0x11223344_55661788), expectRes: Word(0x17_88)}, // lhu $t0, 6($t1) + {name: "lh offset=6 sign-extended", opcode: uint32(0x21), imm: 6, memVal: Word(0x11223344_55668788), expectRes: Word(0xFF_FF_FF_FF_FF_FF_87_88)}, // lhu $t0, 6($t1) + + {name: "lw upper", opcode: uint32(0x23), memVal: Word(0x11223344_55667788), expectRes: Word(0x11223344)}, // lw $t0, 0($t1) + {name: "lw upper sign-extended", opcode: uint32(0x23), memVal: Word(0x81223344_55667788), expectRes: Word(0xFFFFFFFF_81223344)}, // lw $t0, 0($t1) + {name: "lw lower", opcode: uint32(0x23), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55667788)}, // lw $t0, 4($t1) + {name: "lw lower sign-extended", opcode: uint32(0x23), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0xFFFFFFFF_85667788)}, // lw $t0, 4($t1) + + {name: "lbu 0", opcode: uint32(0x24), memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x71)}, // lbu $t0, 0($t1) + {name: "lbu 1", opcode: uint32(0x24), imm: 1, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x72)}, // lbu $t0, 1($t1) + {name: "lbu 2", opcode: uint32(0x24), imm: 2, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x73)}, // lbu $t0, 2($t1) + {name: "lbu 3", opcode: uint32(0x24), imm: 3, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x74)}, // lbu $t0, 3($t1) + {name: "lbu 4", opcode: uint32(0x24), imm: 4, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x75)}, // lbu $t0, 4($t1) + {name: "lbu 5", opcode: uint32(0x24), imm: 5, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x76)}, // lbu $t0, 5($t1) + {name: "lbu 6", opcode: uint32(0x24), imm: 6, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x77)}, // lbu $t0, 6($t1) + {name: "lbu 7", opcode: uint32(0x24), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_78), expectRes: Word(0x78)}, // lbu $t0, 7($t1) + {name: "lbu sign-extended 0", opcode: uint32(0x24), memVal: Word(0x81_72_73_74_75_76_77_78), expectRes: Word(0x81)}, // lbu $t0, 0($t1) + {name: "lbu sign-extended 1", opcode: uint32(0x24), imm: 1, memVal: Word(0x71_82_73_74_75_76_77_78), expectRes: Word(0x82)}, // lbu $t0, 1($t1) + {name: "lbu sign-extended 2", opcode: uint32(0x24), imm: 2, memVal: Word(0x71_72_83_74_75_76_77_78), expectRes: Word(0x83)}, // lbu $t0, 2($t1) + {name: "lbu sign-extended 3", opcode: uint32(0x24), imm: 3, memVal: Word(0x71_72_73_84_75_76_77_78), expectRes: Word(0x84)}, // lbu $t0, 3($t1) + {name: "lbu sign-extended 4", opcode: uint32(0x24), imm: 4, memVal: Word(0x71_72_73_74_85_76_77_78), expectRes: Word(0x85)}, // lbu $t0, 4($t1) + {name: "lbu sign-extended 5", opcode: uint32(0x24), imm: 5, memVal: Word(0x71_72_73_74_75_86_77_78), expectRes: Word(0x86)}, // lbu $t0, 5($t1) + {name: "lbu sign-extended 6", opcode: uint32(0x24), imm: 6, memVal: Word(0x71_72_73_74_75_76_87_78), expectRes: Word(0x87)}, // lbu $t0, 6($t1) + {name: "lbu sign-extended 7", opcode: uint32(0x24), imm: 7, memVal: Word(0x71_72_73_74_75_76_77_88), expectRes: Word(0x88)}, // lbu $t0, 7($t1) + + {name: "lhu offset=0", opcode: uint32(0x25), memVal: Word(0x11223344_55667788), expectRes: Word(0x11_22)}, // lhu $t0, 0($t1) + {name: "lhu offset=0 zero-extended", opcode: uint32(0x25), memVal: Word(0x81223344_55667788), expectRes: Word(0x81_22)}, // lhu $t0, 0($t1) + {name: "lhu offset=2", opcode: uint32(0x25), imm: 2, memVal: Word(0x11223344_55667788), expectRes: Word(0x33_44)}, // lhu $t0, 2($t1) + {name: "lhu offset=2 zero-extended", opcode: uint32(0x25), imm: 2, memVal: Word(0x11228344_55667788), expectRes: Word(0x83_44)}, // lhu $t0, 2($t1) + {name: "lhu offset=4", opcode: uint32(0x25), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55_66)}, // lhu $t0, 4($t1) + {name: "lhu offset=4 zero-extended", opcode: uint32(0x25), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0x85_66)}, // lhu $t0, 4($t1) + {name: "lhu offset=6", opcode: uint32(0x25), imm: 6, memVal: Word(0x11223344_55661788), expectRes: Word(0x17_88)}, // lhu $t0, 6($t1) + {name: "lhu offset=6 zero-extended", opcode: uint32(0x25), imm: 6, memVal: Word(0x11223344_55668788), expectRes: Word(0x87_88)}, // lhu $t0, 6($t1) + + {name: "lwl", opcode: uint32(0x22), rt: Word(0xaa_bb_cc_dd), imm: 4, memVal: Word(0x12_34_56_78), expectRes: Word(0x12_34_56_78)}, // lwl $t0, 4($t1) + {name: "lwl unaligned address", opcode: uint32(0x22), rt: Word(0xaa_bb_cc_dd), imm: 5, memVal: Word(0x12_34_56_78), expectRes: Word(0x34_56_78_dd)}, // lwl $t0, 5($t1) + {name: "lwl offset 0 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_AA_BB_CC_DD)}, // lwl $t0, 0($t1) + {name: "lwl offset 0 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7A_BB_CC_DD)}, // lwl $t0, 0($t1) + {name: "lwl offset 1 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_BB_CC_DD_88)}, // lwl $t0, 1($t1) + {name: "lwl offset 1 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_7B_CC_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7B_CC_DD_88)}, // lwl $t0, 1($t1) + {name: "lwl offset 2 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_CC_DD_77_88)}, // lwl $t0, 2($t1) + {name: "lwl offset 2 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_7C_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7C_DD_77_88)}, // lwl $t0, 2($t1) + {name: "lwl offset 3 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_DD_66_77_88)}, // lwl $t0, 3($t1) + {name: "lwl offset 3 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_7D_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7D_66_77_88)}, // lwl $t0, 3($t1) + {name: "lwl offset 4 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_A1_B1_C1_D1)}, // lwl $t0, 4($t1) + {name: "lwl offset 4 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x00_00_00_00_71_B1_C1_D1)}, // lwl $t0, 4($t1) + {name: "lwl offset 5 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_B1_C1_D1_88)}, // lwl $t0, 5($t1) + {name: "lwl offset 5 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_71_C1_D1), expectRes: Word(0x00_00_00_00_71_C1_D1_88)}, // lwl $t0, 5($t1) + {name: "lwl offset 6 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_C1_D1_77_88)}, // lwl $t0, 6($t1) + {name: "lwl offset 6 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_71_D1), expectRes: Word(0x00_00_00_00_71_D1_77_88)}, // lwl $t0, 6($t1) + {name: "lwl offset 7 sign bit 31 set", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_D1_66_77_88)}, // lwl $t0, 7($t1) + {name: "lwl offset 7 sign bit 31 clear", opcode: uint32(0x22), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_71), expectRes: Word(0x00_00_00_00_71_66_77_88)}, // lwl $t0, 7($t1) + + {name: "lwr zero-extended imm 0 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_7A)}, // lwr $t0, 0($t1) + {name: "lwr zero-extended imm 0 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_AA)}, // lwr $t0, 0($t1) + {name: "lwr zero-extended imm 1 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_7A_BB)}, // lwr $t0, 1($t1) + {name: "lwr zero-extended imm 1 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_AA_BB)}, // lwr $t0, 1($t1) + {name: "lwr zero-extended imm 2 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_7A_BB_CC)}, // lwr $t0, 2($t1) + {name: "lwr zero-extended imm 2 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_AA_BB_CC)}, // lwr $t0, 2($t1) + {name: "lwr sign-extended imm 3 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x00_00_00_00_7A_BB_CC_DD)}, // lwr $t0, 3($t1) + {name: "lwr sign-extended imm 3 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_AA_BB_CC_DD)}, // lwr $t0, 3($t1) + {name: "lwr zero-extended imm 4 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_71)}, // lwr $t0, 4($t1) + {name: "lwr zero-extended imm 4 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_85_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_85_66_77_A1)}, // lwr $t0, 4($t1) + {name: "lwr zero-extended imm 5 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_71_B1)}, // lwr $t0, 5($t1) + {name: "lwr zero-extended imm 5 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_85_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_85_66_A1_B1)}, // lwr $t0, 5($t1) + {name: "lwr zero-extended imm 6 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_71_B1_C1)}, // lwr $t0, 6($t1) + {name: "lwr zero-extended imm 6 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_85_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_85_A1_B1_C1)}, // lwr $t0, 6($t1) + {name: "lwr sign-extended imm 7 sign bit 31 clear", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x00_00_00_00_71_B1_C1_D1)}, // lwr $t0, 7($t1) + {name: "lwr sign-extended imm 7 sign bit 31 set", opcode: uint32(0x26), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xFF_FF_FF_FF_A1_B1_C1_D1)}, // lwr $t0, 7($t1) + + {name: "sb offset=0", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x88_00_00_00_00_00_00_00)}, // sb $t0, 0($t1) + {name: "sb offset=1", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, expectMemVal: Word(0x00_88_00_00_00_00_00_00)}, // sb $t0, 1($t1) + {name: "sb offset=2", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0x00_00_88_00_00_00_00_00)}, // sb $t0, 2($t1) + {name: "sb offset=3", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, expectMemVal: Word(0x00_00_00_88_00_00_00_00)}, // sb $t0, 3($t1) + {name: "sb offset=4", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0x00_00_00_00_88_00_00_00)}, // sb $t0, 4($t1) + {name: "sb offset=5", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, expectMemVal: Word(0x00_00_00_00_00_88_00_00)}, // sb $t0, 5($t1) + {name: "sb offset=6", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0x00_00_00_00_00_00_88_00)}, // sb $t0, 6($t1) + {name: "sb offset=7", opcode: uint32(0x28), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, expectMemVal: Word(0x00_00_00_00_00_00_00_88)}, // sb $t0, 7($t1) + + {name: "sh offset=0", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, expectMemVal: Word(0x77_88_00_00_00_00_00_00)}, // sh $t0, 0($t1) + {name: "sh offset=2", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, expectMemVal: Word(0x00_00_77_88_00_00_00_00)}, // sh $t0, 2($t1) + {name: "sh offset=4", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, expectMemVal: Word(0x00_00_00_00_77_88_00_00)}, // sh $t0, 4($t1) + {name: "sh offset=6", opcode: uint32(0x29), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, expectMemVal: Word(0x00_00_00_00_00_00_77_88)}, // sh $t0, 6($t1) + + {name: "swl offset=0", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // swl $t0, 0($t1) + {name: "swl offset=1", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 1, expectMemVal: Word(0xAA_55_66_77_A1_B1_C1_D1)}, // swl $t0, 1($t1) + {name: "swl offset=2", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 2, expectMemVal: Word(0xAA_BB_55_66_A1_B1_C1_D1)}, // swl $t0, 2($t1) + {name: "swl offset=3", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 3, expectMemVal: Word(0xAA_BB_CC_55_A1_B1_C1_D1)}, // swl $t0, 3($t1) + {name: "swl offset=4", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_55_66_77_88)}, // swl $t0, 4($t1) + {name: "swl offset=5", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 5, expectMemVal: Word(0xAA_BB_CC_DD_A1_55_66_77)}, // swl $t0, 5($t1) + {name: "swl offset=6", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 6, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_55_66)}, // swl $t0, 6($t1) + {name: "swl offset=7", opcode: uint32(0x2a), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 7, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_C1_55)}, // swl $t0, 7($t1) + + {name: "sw offset=0", opcode: uint32(0x2b), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // sw $t0, 0($t1) + {name: "sw offset=4", opcode: uint32(0x2b), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_55_66_77_88)}, // sw $t0, 4($t1) + + {name: "swr offset=0", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x88_BB_CC_DD_A1_B1_C1_D1)}, // swr $t0, 0($t1) + {name: "swr offset=1", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x77_88_CC_DD_A1_B1_C1_D1)}, // swr $t0, 1($t1) + {name: "swr offset=2", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x66_77_88_DD_A1_B1_C1_D1)}, // swr $t0, 2($t1) + {name: "swr offset=3", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // swr $t0, 3($t1) + {name: "swr offset=4", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_88_B1_C1_D1)}, // swr $t0, 4($t1) + {name: "swr offset=5", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_77_88_C1_D1)}, // swr $t0, 5($t1) + {name: "swr offset=6", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_66_77_88_D1)}, // swr $t0, 6($t1) + {name: "swr offset=7", opcode: uint32(0x2e), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0xAA_BB_CC_DD_55_66_77_88)}, // swr $t0, 7($t1) + + // 64-bit instructions + {name: "ldl offset 0 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xAA_BB_CC_DD_A1_B1_C1_D1)}, // ldl $t0, 0($t1) + {name: "ldl offset 1 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xBB_CC_DD_A1_B1_C1_D1_88)}, // ldl $t0, 1($t1) + {name: "ldl offset 2 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xCC_DD_A1_B1_C1_D1_77_88)}, // ldl $t0, 2($t1) + {name: "ldl offset 3 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xDD_A1_B1_C1_D1_66_77_88)}, // ldl $t0, 3($t1) + {name: "ldl offset 4 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xA1_B1_C1_D1_55_66_77_88)}, // ldl $t0, 4($t1) + {name: "ldl offset 5 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xB1_C1_D1_44_55_66_77_88)}, // ldl $t0, 5($t1) + {name: "ldl offset 6 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xC1_D1_33_44_55_66_77_88)}, // ldl $t0, 6($t1) + {name: "ldl offset 7 sign bit 31 set", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xD1_22_33_44_55_66_77_88)}, // ldl $t0, 7($t1) + {name: "ldl offset 0 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x7A_BB_CC_DD_A1_B1_C1_D1)}, // ldl $t0, 0($t1) + {name: "ldl offset 1 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_7B_CC_DD_A1_B1_C1_D1), expectRes: Word(0x7B_CC_DD_A1_B1_C1_D1_88)}, // ldl $t0, 1($t1) + {name: "ldl offset 2 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_7C_DD_A1_B1_C1_D1), expectRes: Word(0x7C_DD_A1_B1_C1_D1_77_88)}, // ldl $t0, 2($t1) + {name: "ldl offset 3 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_7D_A1_B1_C1_D1), expectRes: Word(0x7D_A1_B1_C1_D1_66_77_88)}, // ldl $t0, 3($t1) + {name: "ldl offset 4 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_71_B1_C1_D1), expectRes: Word(0x71_B1_C1_D1_55_66_77_88)}, // ldl $t0, 4($t1) + {name: "ldl offset 5 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_71_C1_D1), expectRes: Word(0x71_C1_D1_44_55_66_77_88)}, // ldl $t0, 5($t1) + {name: "ldl offset 6 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_71_D1), expectRes: Word(0x71_D1_33_44_55_66_77_88)}, // ldl $t0, 6($t1) + {name: "ldl offset 7 sign bit 31 clear", opcode: uint32(0x1A), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_71), expectRes: Word(0x71_22_33_44_55_66_77_88)}, // ldl $t0, 7($t1) + + {name: "ldr offset 0 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_3A)}, // ldr $t0, 0($t1) + {name: "ldr offset 1 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_3A_BB)}, // ldr $t0, 1($t1) + {name: "ldr offset 2 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_3A_BB_CC)}, // ldr $t0, 2($t1) + {name: "ldr offset 3 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_3A_BB_CC_DD)}, // ldr $t0, 3($t1) + {name: "ldr offset 4 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_3A_BB_CC_DD_A1)}, // ldr $t0, 4($t1) + {name: "ldr offset 5 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_3A_BB_CC_DD_A1_B1)}, // ldr $t0, 5($t1) + {name: "ldr offset 6 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_3A_BB_CC_DD_A1_B1_C1)}, // ldr $t0, 6($t1) + {name: "ldr offset 7 sign bit clear", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0x3A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x3A_BB_CC_DD_A1_B1_C1_D1)}, // ldr $t0, 7($t1) + {name: "ldr offset 0 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_77_AA)}, // ldr $t0, 0($t1) + {name: "ldr offset 1 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_66_AA_BB)}, // ldr $t0, 1($t1) + {name: "ldr offset 2 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_55_AA_BB_CC)}, // ldr $t0, 2($t1) + {name: "ldr offset 3 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_44_AA_BB_CC_DD)}, // ldr $t0, 3($t1) + {name: "ldr offset 4 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_33_AA_BB_CC_DD_A1)}, // ldr $t0, 4($t1) + {name: "ldr offset 5 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_22_AA_BB_CC_DD_A1_B1)}, // ldr $t0, 5($t1) + {name: "ldr offset 6 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x11_AA_BB_CC_DD_A1_B1_C1)}, // ldr $t0, 6($t1) + {name: "ldr offset 7 sign bit set", opcode: uint32(0x1b), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0xAA_BB_CC_DD_A1_B1_C1_D1)}, // ldr $t0, 7($t1) + + {name: "lwu upper", opcode: uint32(0x27), memVal: Word(0x11223344_55667788), expectRes: Word(0x11223344)}, // lw $t0, 0($t1) + {name: "lwu upper sign", opcode: uint32(0x27), memVal: Word(0x81223344_55667788), expectRes: Word(0x81223344)}, // lw $t0, 0($t1) + {name: "lwu lower", opcode: uint32(0x27), imm: 4, memVal: Word(0x11223344_55667788), expectRes: Word(0x55667788)}, // lw $t0, 4($t1) + {name: "lwu lower sign", opcode: uint32(0x27), imm: 4, memVal: Word(0x11223344_85667788), expectRes: Word(0x85667788)}, // lw $t0, 4($t1) + + {name: "sdl offset=0", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 0, expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sdl $t0, 0($t1) + {name: "sdl offset=1", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 1, expectMemVal: Word(0xAA_11_22_33_44_55_66_77)}, // sdl $t0, 1($t1) + {name: "sdl offset=2", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 2, expectMemVal: Word(0xAA_BB_11_22_33_44_55_66)}, // sdl $t0, 2($t1) + {name: "sdl offset=3", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 3, expectMemVal: Word(0xAA_BB_CC_11_22_33_44_55)}, // sdl $t0, 3($t1) + {name: "sdl offset=4", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 4, expectMemVal: Word(0xAA_BB_CC_DD_11_22_33_44)}, // sdl $t0, 4($t1) + {name: "sdl offset=5", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 5, expectMemVal: Word(0xAA_BB_CC_DD_A1_11_22_33)}, // sdl $t0, 5($t1) + {name: "sdl offset=6", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 6, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_11_22)}, // sdl $t0, 6($t1) + {name: "sdl offset=7", opcode: uint32(0x2c), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), imm: 7, expectMemVal: Word(0xAA_BB_CC_DD_A1_B1_C1_11)}, // sdl $t0, 7($t1) + + {name: "sdr offset=0", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 0, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x88_BB_CC_DD_A1_B1_C1_D1)}, // sdr $t0, 0($t1) + {name: "sdr offset=1", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 1, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x77_88_CC_DD_A1_B1_C1_D1)}, // sdr $t0, 1($t1) + {name: "sdr offset=2", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 2, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x66_77_88_DD_A1_B1_C1_D1)}, // sdr $t0, 2($t1) + {name: "sdr offset=3", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 3, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x55_66_77_88_A1_B1_C1_D1)}, // sdr $t0, 3($t1) + {name: "sdr offset=4", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 4, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x44_55_66_77_88_B1_C1_D1)}, // sdr $t0, 4($t1) + {name: "sdr offset=5", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 5, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x33_44_55_66_77_88_C1_D1)}, // sdr $t0, 5($t1) + {name: "sdr offset=6", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 6, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x22_33_44_55_66_77_88_D1)}, // sdr $t0, 6($t1) + {name: "sdr offset=7", opcode: uint32(0x2d), rt: Word(0x11_22_33_44_55_66_77_88), imm: 7, memVal: Word(0xAA_BB_CC_DD_A1_B1_C1_D1), expectMemVal: Word(0x11_22_33_44_55_66_77_88)}, // sdr $t0, 7($t1) + + {name: "ld", opcode: uint32(0x37), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0x7A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x7A_BB_CC_DD_A1_B1_C1_D1)}, // ld $t0, 0($t1) + {name: "ld signed", opcode: uint32(0x37), rt: Word(0x11_22_33_44_55_66_77_88), memVal: Word(0x8A_BB_CC_DD_A1_B1_C1_D1), expectRes: Word(0x8A_BB_CC_DD_A1_B1_C1_D1)}, // ld $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) + } + + v := GetMultiThreadedTestCase(t) + var t1 Word = 0xFF000000_00000108 + var baseReg uint32 = 9 + var rtReg uint32 = 8 + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + effAddr := arch.AddressMask & t1 + + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + + insn := tt.opcode<<26 | baseReg<<21 | rtReg<<16 | uint32(tt.imm) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[baseReg] = t1 + + state.GetMemory().SetUint32(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, nil) + }) + } +} + +func TestEVMSingleStep_DivMult(t *testing.T) { + cases := []struct { + name string + rs Word + rt Word + funct uint32 + expectLo Word + expectHi Word + expectPanic string + }{ + // dmult s1, s2 + // expected hi,lo were verified using qemu-mips + {name: "dmult 0", funct: 0x1c, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, + {name: "dmult 1", funct: 0x1c, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "dmult 2", funct: 0x1c, rs: 0x01_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00, expectHi: 0}, + {name: "dmult 3", funct: 0x1c, rs: 0x01_00_00_00_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00_00_00_00, expectHi: 0}, + {name: "dmult 4", funct: 0x1c, rs: 0x40_00_00_00_00_00_00_00, rt: 2, expectLo: 0x80_00_00_00_00_00_00_00, expectHi: 0x0}, + {name: "dmult 5", funct: 0x1c, rs: 0x40_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0x4_00}, + {name: "dmult 6", funct: 0x1c, rs: 0x80_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0xFF_FF_FF_FF_FF_FF_F8_00}, + {name: "dmult 7", funct: 0x1c, rs: 0x80_00_00_00_00_00_00_00, rt: 0x80_00_00_00_00_00_00_00, expectLo: 0x0, expectHi: 0x40_00_00_00_00_00_00_00}, + {name: "dmult 8", funct: 0x1c, rs: 0x40_00_00_00_00_00_00_01, rt: 0x1000, expectLo: 0x1000, expectHi: 0x4_00}, + {name: "dmult 9", funct: 0x1c, rs: 0x80_00_00_00_00_00_00_80, rt: 0x80_00_00_00_00_00_00_80, expectLo: 0x4000, expectHi: 0x3F_FF_FF_FF_FF_FF_FF_80}, + {name: "dmult 10", funct: 0x1c, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x1), expectLo: 0xFF_FF_FF_FF_FF_FF_FF_FF, expectHi: 0xFF_FF_FF_FF_FF_FF_FF_FF}, + {name: "dmult 11", funct: 0x1c, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectLo: 0x1, expectHi: Word(0)}, + {name: "dmult 12", funct: 0x1c, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E0), expectLo: 0xFC_FC_FD_0A_8E_20_EB_A0, expectHi: 0x00_00_00_00_00_00_00_0E}, + {name: "dmult 13", funct: 0x1c, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E1), expectLo: 0xD5_44_33_22_5E_2E_3E_1F, expectHi: 0xD5_5D_E6_6E_D0_E8_E0_F0}, + {name: "dmult 14", funct: 0x1c, 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: 0xC7_FF_FF_FF_FF_FF_FF_FF}, + + // dmultu s1, s2 + {name: "dmultu 0", funct: 0x1d, rs: 0, rt: 0, expectLo: 0, expectHi: 0}, + {name: "dmultu 1", funct: 0x1d, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "dmultu 2", funct: 0x1d, rs: 0x01_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00, expectHi: 0}, + {name: "dmultu 3", funct: 0x1d, rs: 0x01_00_00_00_00_00_00_00, rt: 2, expectLo: 0x02_00_00_00_00_00_00_00, expectHi: 0}, + {name: "dmultu 4", funct: 0x1d, rs: 0x40_00_00_00_00_00_00_00, rt: 2, expectLo: 0x80_00_00_00_00_00_00_00, expectHi: 0x0}, + {name: "dmultu 5", funct: 0x1d, rs: 0x40_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0x4_00}, + {name: "dmultu 6", funct: 0x1d, rs: 0x80_00_00_00_00_00_00_00, rt: 0x1000, expectLo: 0x0, expectHi: 0x8_00}, + {name: "dmultu 7", funct: 0x1d, rs: 0x80_00_00_00_00_00_00_00, rt: 0x80_00_00_00_00_00_00_00, expectLo: 0x0, expectHi: 0x40_00_00_00_00_00_00_00}, + {name: "dmultu 8", funct: 0x1d, rs: 0x40_00_00_00_00_00_00_01, rt: 0x1000, expectLo: 0x1000, expectHi: 0x4_00}, + {name: "dmultu 9", funct: 0x1d, rs: 0x80_00_00_00_00_00_00_80, rt: 0x80_00_00_00_00_00_00_80, expectLo: 0x4000, expectHi: 0x40_00_00_00_00_00_00_80}, + {name: "dmultu 10", funct: 0x1d, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectLo: 0x1, expectHi: Word(0xFF_FF_FF_FF_FF_FF_FF_FE)}, + {name: "dmultu 11", funct: 0x1d, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectLo: 0x1, expectHi: 0xFF_FF_FF_FF_FF_FF_FF_FE}, + {name: "dmultu 12", funct: 0x1d, rs: Word(0xFF_FF_FF_FF_FF_FF_FF_D3), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E0), expectLo: 0xFC_FC_FD_0A_8E_20_EB_A0, expectHi: 0xAA_BB_CC_DD_A1_D1_C1_C1}, + {name: "dmultu 13", funct: 0x1d, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0xAA_BB_CC_DD_A1_D1_C1_E1), expectLo: 0xD5_44_33_22_5E_2E_3E_1F, expectHi: 0x55_5D_E6_6E_D0_E8_E0_EF}, + {name: "dmultu 14", funct: 0x1d, rs: Word(0x7F_FF_FF_FF_FF_FF_FF_FF), rt: Word(0x8F_FF_FF_FF_FF_FF_FF_FF), expectLo: 0xF0_00_00_00_00_00_00_01, expectHi: 0x47_FF_FF_FF_FF_FF_FF_FE}, + + // ddiv rs, rt + {name: "ddiv", funct: 0x1e, rs: 0, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 1, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddiv", funct: 0x1e, rs: 0, rt: 1, expectLo: 0, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: 10, rt: 3, expectLo: 3, expectHi: 1}, + {name: "ddiv", funct: 0x1e, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0x3F_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: 0xFF_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0xFF_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: ^Word(0), rt: ^Word(0), expectLo: 1, expectHi: 0}, + {name: "ddiv", funct: 0x1e, rs: ^Word(0), rt: 2, expectLo: 0, expectHi: ^Word(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 + {name: "ddivu", funct: 0x1f, rs: 0, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 1, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_FF_FF_FF_FF, rt: 0, expectPanic: "instruction divide by zero"}, + {name: "ddivu", funct: 0x1f, rs: 0, rt: 1, expectLo: 0, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: 1, rt: 1, expectLo: 1, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: 10, rt: 3, expectLo: 3, expectHi: 1}, + {name: "ddivu", funct: 0x1f, rs: 0x7F_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0x3F_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: 0xFF_FF_FF_FF_00_00_00_00, rt: 2, expectLo: 0x7F_FF_FF_FF_80_00_00_00, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: ^Word(0), expectLo: 1, expectHi: 0}, + {name: "ddivu", funct: 0x1f, rs: ^Word(0), rt: 2, expectLo: 0x7F_FF_FF_FF_FF_FF_FF_FF, expectHi: 1}, + {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}, + } + + v := GetMultiThreadedTestCase(t) + for i, tt := range cases { + testName := fmt.Sprintf("%v %v", v.Name, tt.name) + t.Run(testName, func(t *testing.T) { + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i)), testutil.WithPCAndNextPC(0)) + state := goVm.GetState() + var rsReg uint32 = 17 + var rtReg uint32 = 18 + insn := rsReg<<21 | rtReg<<16 | tt.funct + state.GetRegistersRef()[rsReg] = tt.rs + state.GetRegistersRef()[rtReg] = tt.rt + state.GetMemory().SetUint32(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, nil) + } + }) + } +} + +func TestEVMSingleStepBranch64(t *testing.T) { + var tracer *tracing.Hooks + + versions := GetMipsVersionTestCases(t) + cases := []struct { + name string + pc Word + expectNextPC Word + opcode uint32 + regimm uint32 + expectLink bool + rs arch.SignedInteger + rt Word + offset uint16 + }{ + // blez + {name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "blez zero rs", pc: 0x10, opcode: 0x6, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign rs", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "blez rs only sign bit set", pc: 0x10, opcode: 0x6, rs: testutil.ToSignedInteger(0x80_00_00_00_00_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign-extended offset", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + + // bgtz + {name: "bgtz", pc: 0, opcode: 0x7, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgtz sign-extended offset", pc: 0x10, opcode: 0x7, rs: 0x5, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + {name: "bgtz large rs", pc: 0x10, opcode: 0x7, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgtz zero rs", pc: 0x10, opcode: 0x7, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz sign rs", pc: 0x10, opcode: 0x7, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz rs only sign bit set", pc: 0x10, opcode: 0x7, rs: testutil.ToSignedInteger(0x80_00_00_00_00_00_00_00), offset: 0x100, expectNextPC: 0x18}, + + // bltz t0, $x + {name: "bltz", pc: 0, opcode: 0x1, regimm: 0x0, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "bltz large rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz zero rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz sign rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "bltz rs only sign bit set", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: testutil.ToSignedInteger(0x80_00_00_00_00_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "bltz sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + {name: "bltz large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgez t0, $x + {name: "bgez", pc: 0, opcode: 0x1, regimm: 0x1, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgez large rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez zero rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgez sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14}, + {name: "bgez large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14}, + {name: "bgez fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgezal t0, $x + {name: "bgezal", pc: 0, opcode: 0x1, regimm: 0x11, rs: 0x5, offset: 0x100, expectNextPC: 0x404, expectLink: true}, + {name: "bgezal large rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x7F_FF_FF_FF_FF_FF_FF_FF, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal zero rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x0, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: -1, offset: 0x100, expectNextPC: 0x18, expectLink: true}, + {name: "bgezal sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FF_FF_FF_FF_FE_00_14, expectLink: true}, + {name: "bgezal large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14, 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 { + 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) + state.GetMemory().SetUint32(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, tracer) + }) + } + } +} diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index c9dfd2f096343..f1e08e6eda367 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -991,3 +991,94 @@ func TestEntryEVM(t *testing.T) { }) } } + +func TestEVMSingleStepBranch(t *testing.T) { + var tracer *tracing.Hooks + + versions := GetMipsVersionTestCases(t) + cases := []struct { + name string + pc Word + expectNextPC Word + opcode uint32 + regimm uint32 + expectLink bool + rs arch.SignedInteger + rt Word + offset uint16 + }{ + // blez + {name: "blez", pc: 0, opcode: 0x6, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "blez large rs", pc: 0x10, opcode: 0x6, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "blez zero rs", pc: 0x10, opcode: 0x6, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign rs", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "blez rs only sign bit set", pc: 0x10, opcode: 0x6, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "blez sign-extended offset", pc: 0x10, opcode: 0x6, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + + // bgtz + {name: "bgtz", pc: 0, opcode: 0x7, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgtz sign-extended offset", pc: 0x10, opcode: 0x7, rs: 0x5, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + {name: "bgtz large rs", pc: 0x10, opcode: 0x7, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgtz zero rs", pc: 0x10, opcode: 0x7, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz sign rs", pc: 0x10, opcode: 0x7, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgtz rs only sign bit set", pc: 0x10, opcode: 0x7, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x18}, + + // bltz t0, $x + {name: "bltz", pc: 0, opcode: 0x1, regimm: 0x0, rs: 0x5, offset: 0x100, expectNextPC: 0x8}, + {name: "bltz large rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz zero rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: 0x0, offset: 0x100, expectNextPC: 0x18}, + {name: "bltz sign rs", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x100, expectNextPC: 0x414}, + {name: "bltz rs only sign bit set", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: testutil.ToSignedInteger(0x80_00_00_00), offset: 0x100, expectNextPC: 0x414}, + {name: "bltz sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + {name: "bltz large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x0, rs: -1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgez t0, $x + {name: "bgez", pc: 0, opcode: 0x1, regimm: 0x1, rs: 0x5, offset: 0x100, expectNextPC: 0x404}, + {name: "bgez large rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez zero rs", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 0x0, offset: 0x100, expectNextPC: 0x414}, + {name: "bgez branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: -1, offset: 0x100, expectNextPC: 0x18}, + {name: "bgez sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14}, + {name: "bgez large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14}, + {name: "bgez fill bit offset except sign", pc: 0x10, opcode: 0x1, regimm: 0x1, rs: 1, offset: 0x7F_FF, expectNextPC: 0x2_00_10}, + + // bgezal t0, $x + {name: "bgezal", pc: 0, opcode: 0x1, regimm: 0x11, rs: 0x5, offset: 0x100, expectNextPC: 0x404, expectLink: true}, + {name: "bgezal large rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x7F_FF_FF_FF, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal zero rs", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 0x0, offset: 0x100, expectNextPC: 0x414, expectLink: true}, + {name: "bgezal branch not taken", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: -1, offset: 0x100, expectNextPC: 0x18, expectLink: true}, + {name: "bgezal sign-extended offset", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x80_00, expectNextPC: 0xFF_FE_00_14, expectLink: true}, + {name: "bgezal large offset no-sign", pc: 0x10, opcode: 0x1, regimm: 0x11, rs: 1, offset: 0x70_00, expectNextPC: 0x1_C0_14, 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 { + 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) + state.GetMemory().SetUint32(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, tracer) + }) + } + } +} diff --git a/cannon/mipsevm/tests/evm_multithreaded_test.go b/cannon/mipsevm/tests/evm_multithreaded_test.go index 6c297ea946d05..0ea9142a91687 100644 --- a/cannon/mipsevm/tests/evm_multithreaded_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded_test.go @@ -1011,7 +1011,7 @@ func testEVM_SysClockGettime(t *testing.T, clkid Word) { goVm, state, contracts := setup(t, 2101, nil) mttestutil.InitializeSingleThread(2101+i, state, i%2 == 1) effAddr := c.timespecAddr & arch.AddressMask - effAddr2 := effAddr + 4 + effAddr2 := effAddr + arch.WordSizeBytes step := state.Step // Define LL-related params @@ -1121,6 +1121,8 @@ var NoopSyscalls = map[string]uint32{ "SysLlseek": 4140, "SysMinCore": 4217, "SysTgkill": 4266, + "SysGetRLimit": 4076, + "SysLseek": 4019, "SysMunmap": 4091, "SysSetITimer": 4104, "SysTimerCreate": 4257, diff --git a/cannon/mipsevm/testutil/arch.go b/cannon/mipsevm/testutil/arch.go index 2efa75eafcec6..a69896c041e57 100644 --- a/cannon/mipsevm/testutil/arch.go +++ b/cannon/mipsevm/testutil/arch.go @@ -40,3 +40,9 @@ func SetMemoryUint64(t require.TestingT, mem *memory.Memory, addr Word, value ui actual := mem.GetWord(effAddr) require.Equal(t, Word(value), actual) } + +// ToSignedInteger converts the unsigend Word to a SignedInteger. +// Useful for avoiding Go compiler warnings for literals that don't fit in a signed type +func ToSignedInteger(x Word) arch.SignedInteger { + return arch.SignedInteger(x) +} diff --git a/cannon/mipsevm/testutil/elf.go b/cannon/mipsevm/testutil/elf.go index b5b63cdeb1d39..3e1896963cda4 100644 --- a/cannon/mipsevm/testutil/elf.go +++ b/cannon/mipsevm/testutil/elf.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" ) @@ -26,3 +27,12 @@ func LoadELFProgram[T mipsevm.FPVMState](t require.TestingT, name string, initSt require.NoError(t, program.PatchStack(state), "add initial stack") return state, meta } + +// ProgramPath returns the appropriate ELF test program for the current architecture +func ProgramPath(programName string) string { + basename := programName + ".elf" + if !arch.IsMips32 { + basename = programName + ".64.elf" + } + return "../../testdata/example/bin/" + basename +} diff --git a/cannon/mipsevm/testutil/oracle.go b/cannon/mipsevm/testutil/oracle.go index 64b0f31a897c9..b5c4f7dce9151 100644 --- a/cannon/mipsevm/testutil/oracle.go +++ b/cannon/mipsevm/testutil/oracle.go @@ -67,7 +67,7 @@ func StaticPrecompileOracle(t *testing.T, precompile common.Address, requiredGas } func ClaimTestOracle(t *testing.T) (po mipsevm.PreimageOracle, stdOut string, stdErr string) { - s := uint64(1000) + s := uint64(0x00FFFFFF_00001000) a := uint64(3) b := uint64(4) diff --git a/cannon/mipsevm/testutil/vmtests.go b/cannon/mipsevm/testutil/vmtests.go index 382c608333998..333720a4ec025 100644 --- a/cannon/mipsevm/testutil/vmtests.go +++ b/cannon/mipsevm/testutil/vmtests.go @@ -16,10 +16,14 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" ) -type VMFactory[T mipsevm.FPVMState] func(state T, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger) mipsevm.FPVM +type VMFactory[T mipsevm.FPVMState] func(state T, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta *program.Metadata) mipsevm.FPVM type StateFactory[T mipsevm.FPVMState] func() T func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFactory[T], vmFactory VMFactory[T], excludedTests ...string) { + if !arch.IsMips32 { + // TODO: guard these tests by the cannon32 build tag + t.Skip("Open MIPS tests are not appropriate for cannon64") + } testFiles, err := os.ReadDir("../tests/open_mips_tests/test/bin") require.NoError(t, err) @@ -52,7 +56,7 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa // set the return address ($ra) to jump into when test completes state.GetRegistersRef()[31] = EndAddr - us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger()) + us := vmFactory(state, oracle, os.Stdout, os.Stderr, CreateLogger(), nil) // Catch panics and check if they are expected defer func() { @@ -94,12 +98,13 @@ func RunVMTests_OpenMips[T mipsevm.FPVMState](t *testing.T, stateFactory StateFa } func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) { - state, _ := LoadELFProgram(t, "../../testdata/example/bin/hello.elf", initState, doPatchGo) + state, meta := LoadELFProgram(t, ProgramPath("hello"), initState, doPatchGo) var stdOutBuf, stdErrBuf bytes.Buffer - us := vmFactory(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger()) + us := vmFactory(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger(), meta) - for i := 0; i < 400_000; i++ { + maxSteps := 430_000 + for i := 0; i < maxSteps; i++ { if us.GetState().GetExited() { break } @@ -107,7 +112,7 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create require.NoError(t, err) } - require.True(t, state.GetExited(), "must complete program") + require.Truef(t, state.GetExited(), "must complete program. reached %d of max %d steps", state.GetStep(), maxSteps) require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0") require.Equal(t, "hello world!\n", stdOutBuf.String(), "stdout says hello") @@ -115,12 +120,12 @@ func RunVMTest_Hello[T mipsevm.FPVMState](t *testing.T, initState program.Create } func RunVMTest_Claim[T mipsevm.FPVMState](t *testing.T, initState program.CreateInitialFPVMState[T], vmFactory VMFactory[T], doPatchGo bool) { - state, _ := LoadELFProgram(t, "../../testdata/example/bin/claim.elf", initState, doPatchGo) + state, meta := LoadELFProgram(t, ProgramPath("claim"), initState, doPatchGo) oracle, expectedStdOut, expectedStdErr := ClaimTestOracle(t) var stdOutBuf, stdErrBuf bytes.Buffer - us := vmFactory(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger()) + us := vmFactory(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), CreateLogger(), meta) for i := 0; i < 2000_000; i++ { if us.GetState().GetExited() { diff --git a/cannon/testdata/example/Makefile b/cannon/testdata/example/Makefile index 7b3b8fdf019d2..5b0dee96eb9ad 100644 --- a/cannon/testdata/example/Makefile +++ b/cannon/testdata/example/Makefile @@ -1,7 +1,13 @@ all: elf +.PHONY: elf32 +elf32: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod)) + +.PHONY: elf64 +elf64: $(patsubst %/go.mod,bin/%.64.elf,$(wildcard */go.mod)) + .PHONY: elf -elf: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod)) +elf: elf32 elf64 .PHONY: dump dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod)) @@ -9,6 +15,9 @@ dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod)) bin: mkdir bin +bin/%.64.elf: bin + cd $(@:bin/%.64.elf=%) && GOOS=linux GOARCH=mips64 GOMIPS64=softfloat go build -o ../$@ . + # take any directory with a go mod, and build an ELF # verify output with: readelf -h bin/.elf # result is mips32, big endian, R3000 diff --git a/cannon/testdata/example/alloc/go.mod b/cannon/testdata/example/alloc/go.mod index a5dd16ce8b273..c364668acc106 100644 --- a/cannon/testdata/example/alloc/go.mod +++ b/cannon/testdata/example/alloc/go.mod @@ -1,6 +1,6 @@ module alloc -go 1.22 +go 1.22.0 toolchain go1.22.7 diff --git a/cannon/testdata/example/claim/go.mod b/cannon/testdata/example/claim/go.mod index dd76391de26ba..e3904911a172d 100644 --- a/cannon/testdata/example/claim/go.mod +++ b/cannon/testdata/example/claim/go.mod @@ -1,6 +1,6 @@ module claim -go 1.22 +go 1.22.0 toolchain go1.22.7 diff --git a/cannon/testdata/example/hello/go.mod b/cannon/testdata/example/hello/go.mod index b54bb78c6aee6..f036b5dc97fce 100644 --- a/cannon/testdata/example/hello/go.mod +++ b/cannon/testdata/example/hello/go.mod @@ -1,5 +1,5 @@ module hello -go 1.22 +go 1.22.0 -toolchain go1.22.0 +toolchain go1.22.7 diff --git a/go.mod b/go.mod index 7478f4a185288..ab9e5725ce878 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum-optimism/optimism -go 1.22 +go 1.22.0 toolchain go1.22.7 @@ -47,11 +47,10 @@ require ( github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.5 golang.org/x/crypto v0.28.0 - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c golang.org/x/sync v0.8.0 golang.org/x/term v0.25.0 golang.org/x/time v0.7.0 - lukechampine.com/uint128 v1.3.0 ) require ( @@ -95,7 +94,7 @@ require ( github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/felixge/fgprof v0.9.3 // indirect + github.com/felixge/fgprof v0.9.5 // indirect github.com/ferranbt/fastssz v0.1.2 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect @@ -114,7 +113,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect + github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect @@ -234,11 +233,11 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/mod v0.20.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.26.0 // indirect google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect diff --git a/go.sum b/go.sum index 7b74fd565d0fb..de095722c8b59 100644 --- a/go.sum +++ b/go.sum @@ -89,12 +89,18 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= @@ -192,8 +198,9 @@ github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= +github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -242,6 +249,9 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -299,8 +309,9 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA= +github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -363,6 +374,7 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= @@ -394,6 +406,7 @@ github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZl github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -443,6 +456,7 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= @@ -473,6 +487,7 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= @@ -593,6 +608,7 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= @@ -846,8 +862,8 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -860,8 +876,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1021,8 +1037,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1094,8 +1110,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 45944697945e2..5b62da033bf0b 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -140,12 +140,12 @@ "sourceCodeHash": "0x2ab6be69795109a1ee04c5693a34d6ce0ff90b62e404cdeb18178bab18d06784" }, "src/cannon/MIPS.sol": { - "initCodeHash": "0x8fb590d45fa06fdc7ae55860827d6b1abf070c9dc5e18cd28176e844fa1da6c9", - "sourceCodeHash": "0x01d3d59020ec29ce78f68f977da5b7754455743121bda65626509864ab6c9da9" + "initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2", + "sourceCodeHash": "0x3c2e5d6d39b29a60dcd1a776363518c87fcfef9a8d80e38696f56b6eec5bd53a" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0x88acf3297642c2c9e0c1e92b48f59f7015915f25fb816cac44ee0073a1c93ccb", - "sourceCodeHash": "0x3dd89839f268569cbf2659beb42e3ac3c53887472cfc94a6e339d2b3a963b941" + "initCodeHash": "0x5c292882075bd06e68b89119be9096040bc913f51bb878ce4a9dfdd674330dd5", + "sourceCodeHash": "0x17c7b0edb9d20932eaf1b038e3e05e457f0461d2c8691ba1940fb4c2b0dfd123" }, "src/cannon/PreimageOracle.sol": { "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", diff --git a/packages/contracts-bedrock/src/cannon/MIPS.sol b/packages/contracts-bedrock/src/cannon/MIPS.sol index 337a2d47ee2b4..38989cfc7b7b0 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS.sol @@ -44,8 +44,8 @@ contract MIPS is ISemver { } /// @notice The semantic version of the MIPS contract. - /// @custom:semver 1.2.1-beta.4 - string public constant version = "1.2.1-beta.4"; + /// @custom:semver 1.2.1-beta.5 + string public constant version = "1.2.1-beta.5"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index 7946cd0c3362f..3f5044f14e04c 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -60,8 +60,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.17 - string public constant version = "1.0.0-beta.17"; + /// @custom:semver 1.0.0-beta.18 + string public constant version = "1.0.0-beta.18"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; @@ -593,6 +593,10 @@ contract MIPS2 is ISemver { // ignored } else if (syscall_no == sys.SYS_TIMERDELETE) { // ignored + } else if (syscall_no == sys.SYS_GETRLIMIT) { + // ignored + } else if (syscall_no == sys.SYS_LSEEK) { + // ignored } else { if (syscall_no == sys.SYS_FSTAT64 || syscall_no == sys.SYS_STAT64 || syscall_no == sys.SYS_LLSEEK) { // noop diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol index a2402531c82d1..eab4a61db65a9 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol @@ -7,6 +7,7 @@ import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; library MIPSInstructions { uint32 internal constant OP_LOAD_LINKED = 0x30; uint32 internal constant OP_STORE_CONDITIONAL = 0x38; + uint32 internal constant REG_RA = 31; struct CoreStepLogicParams { /// @param opcode The opcode value parsed from insn_. @@ -501,6 +502,11 @@ library MIPSInstructions { if (rtv == 1) { shouldBranch = int32(_rs) >= 0; } + // bgezal (i.e. bal mnemonic) + if (rtv == 0x11) { + shouldBranch = int32(_rs) >= 0; + _registers[REG_RA] = _cpu.pc + 8; // always set regardless of branch taken + } } // Update the state's previous PC diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol index 9b5278e052aeb..4354163a9d3f7 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol @@ -71,6 +71,8 @@ library MIPSSyscalls { uint32 internal constant SYS_LLSEEK = 4140; uint32 internal constant SYS_MINCORE = 4217; uint32 internal constant SYS_TGKILL = 4266; + uint32 internal constant SYS_GETRLIMIT = 4076; + uint32 internal constant SYS_LSEEK = 4019; // profiling-related syscalls - ignored uint32 internal constant SYS_SETITIMER = 4104;