diff --git a/cannon/mipsevm/README.md b/cannon/mipsevm/README.md index f01aa61510e54..1e3b4e516dec6 100644 --- a/cannon/mipsevm/README.md +++ b/cannon/mipsevm/README.md @@ -19,8 +19,22 @@ Supported 63 instructions: | `Conditional Branch` | `bne` | Branch on not equal. | | `Logical` | `clo` | Count leading ones. | | `Logical` | `clz` | Count leading zeros. | +| `Logical` | `dclz` | Count Leading Zeros in Doubleword. | +| `Bit Manipulation` | `dext` | Doubleword Extract Bit Field. | +| `Bit Manipulation` | `dextm` | Doubleword Extract Bit Field Middle. | +| `Bit Manipulation` | `dextu` | Doubleword Extract Bit Field Upper. | +| `Bit Manipulation` | `dins` | Doubleword Insert Bit Field. | +| `Bit Manipulation` | `dinsm` | Doubleword Insert Bit Field Middle. | +| `Bit Manipulation` | `dinsu` | Doubleword Insert Bit Field Upper. | | `Arithmetic` | `div` | Divide. | | `Arithmetic` | `divu` | Divide unsigned. | +| `Logical` | `drotr` | Doubleword Rotate Right. | +| `Logical` | `drotr32` | Doubleword Rotate Right Plus 32. | +| `Logical` | `drotrv` | Doubleword Rotate Right Variable. | +| `Bit Manipulation` | `dsbh` | Doubleword Swap Bytes Within Halfwords. | +| `Bit Manipulation` | `dshd` | Doubleword Swap Halfwords Within Doublewords.| +| `Bit Manipulation` | `ext` | Extract Bit Field. | +| `Bit Manipulation` | `ins` | Insert Bit Field. | | `Unconditional Jump` | `j` | Jump. | | `Unconditional Jump` | `jal` | Jump and link. | | `Unconditional Jump` | `jalr` | Jump and link register. | @@ -46,8 +60,12 @@ Supported 63 instructions: | `Logical` | `nor` | Bitwise NOR. | | `Logical` | `or` | Bitwise OR. | | `Logical` | `ori` | Bitwise OR immediate. | +| `Logical` | `rotr` | Rotate Word Right. | +| `Logical` | `rotrv` | Rotate Word Right Variable. | | `Data Transfer` | `sb` | Store byte. | | `Data Transfer` | `sc` | Store conditional. | +| `Arithmetic` | `seb` | Sign-Extend Byte. | +| `Arithmetic` | `seh` | Sign-Extend Halfword. | | `Data Transfer` | `sh` | Store halfword. | | `Logical` | `sll` | Shift left logical. | | `Logical` | `sllv` | Shift left logical variable. | @@ -66,6 +84,7 @@ Supported 63 instructions: | `Data Transfer` | `swr` | Store word right. | | `Serialization` | `sync` | Synchronize shared memory. | | `System Calls` | `syscall` | System call. | +| `Bit Manipulation` | `wsbh` | Word Swap Bytes Within Halfwords. | | `Logical` | `xor` | Bitwise XOR. | | `Logical` | `xori` | Bitwise XOR immediate. | diff --git a/cannon/mipsevm/exec/mips_instructions.go b/cannon/mipsevm/exec/mips_instructions.go index bbb9e8b208770..493e00bbf83e5 100644 --- a/cannon/mipsevm/exec/mips_instructions.go +++ b/cannon/mipsevm/exec/mips_instructions.go @@ -70,6 +70,28 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory // R-type (stores rd) rt = registers[rtReg] rdReg = Word((insn >> 11) & 0x1F) + } else if opcode == 0x1f { // SPECIAL3 + // SPECIAL3 is generally R-type with exceptions + if fun == 0x20 { + // seb, seh, wsbh + // R-type (stores rd) + rt = registers[rtReg] + rdReg = Word((insn >> 11) & 0x1F) + } else if fun == 0x24 { + // dsbh, dshd + // R-type (stores rd) + assertMips64(insn) + rt = registers[rtReg] + rdReg = Word((insn >> 11) & 0x1F) + } else if fun == 0x3 || fun == 0x1 || fun == 0x2 || fun == 0x7 || fun == 0x5 || fun == 0x6 || fun == 0x4 || fun == 0x0 { + // dext, dextm, dextu, dins, dinsm, dinsu, ins, ext + // Exception (stores rt) + if !(fun == 0x4 || fun == 0x0) { + assertMips64(insn) + } + rt = registers[rtReg] + rdReg = Word((insn >> 16) & 0x1F) + } } else if opcode < 0x20 { // rt is SignExtImm // don't sign extend for andi, ori, xori @@ -148,7 +170,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory // lo and hi registers // can write back if fun >= 0x10 && fun < funSel { - err = HandleHiLo(cpu, registers, fun, rs, rt, rdReg) + err = HandleHiLo(cpu, registers, insn, fun, rs, rt, rdReg) return } } @@ -210,16 +232,28 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem case 0x00: // sll shiftAmt := (insn >> 6) & 0x1F return SignExtend((rt<>((insn>>6)&0x1F), 32) + case 0x02: + sa := (insn >> 6) & 0x1F + maskedRt := rt & U32Mask + if (insn>>21)&0x1 == 1 { // rotr + return SignExtend((maskedRt>>sa)|(maskedRt<<(32-sa)), 32) + } else { // srl + return SignExtend(maskedRt>>sa, 32) + } case 0x03: // sra shamt := Word((insn >> 6) & 0x1F) return SignExtend((rt&U32Mask)>>shamt, 32-shamt) case 0x04: // sllv shiftAmt := rs & 0x1F return SignExtend((rt<>(rs&0x1F), 32) + case 0x06: + shift := rs & 0x1F + maskedRt := rt & U32Mask + if (insn>>6)&0x1 == 1 { // rotrv + return SignExtend((maskedRt>>shift)|(maskedRt<<(32-shift)), 32) + } else { // srlv + return SignExtend(maskedRt>>shift, 32) + } case 0x07: // srav shamt := Word(rs & 0x1F) return SignExtend((rt&U32Mask)>>shamt, 32-shamt) @@ -291,12 +325,12 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return rs ^ rt case 0x27: // nor return ^(rs | rt) - case 0x2a: // slti + case 0x2a: // slti, slt(SPECIAL) if arch.SignedInteger(rs) < arch.SignedInteger(rt) { return 1 } return 0 - case 0x2b: // sltiu + case 0x2b: // sltiu, sltu(SPECIAL) if rs < rt { return 1 } @@ -316,18 +350,28 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem case 0x38: // dsll assertMips64(insn) return rt << ((insn >> 6) & 0x1f) - case 0x3A: // dsrl + case 0x3A: assertMips64(insn) - return rt >> ((insn >> 6) & 0x1f) + shift := (insn >> 6) & 0x1f + if (insn>>21)&0x1 == 1 { // drotr + return (rt >> shift) | (rt << (64 - shift)) + } else { // dsrl + return rt >> shift + } case 0x3B: // dsra assertMips64(insn) return Word(int64(rt) >> ((insn >> 6) & 0x1f)) case 0x3C: // dsll32 assertMips64(insn) return rt << (((insn >> 6) & 0x1f) + 32) - case 0x3E: // dsrl32 + case 0x3E: assertMips64(insn) - return rt >> (((insn >> 6) & 0x1f) + 32) + shift := ((insn >> 6) & 0x1f) + 32 + if (insn>>21)&0x1 == 1 { // drotr32 + return (rt >> shift) | (rt << (64 - shift)) + } else { + return rt >> shift // dsrl32 + } case 0x3F: // dsra32 assertMips64(insn) return Word(int64(rt) >> (((insn >> 6) & 0x1f) + 32)) @@ -350,6 +394,9 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem rs <<= 1 } return Word(i) + case 0x24: // dclz + assertMips64(insn) + return Word(bits.LeadingZeros64(uint64(rs))) } case 0x0F: // lui return SignExtend(rt<<16, 32) @@ -425,7 +472,6 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem return UpdateSubWord(rs, mem, 4, Word(swrResult)) } - // MIPS64 case 0x1A: // ldl assertMips64(insn) sl := (rs & 0x7) << 3 @@ -459,6 +505,72 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem case 0x3F: // sd assertMips64(insn) return rt + case 0x1f: // SPECIAL3 + switch fun { + case 0x20: // bshfl + switch (insn >> 6) & 0x1f { + case 0x10: // seb + return SignExtend(rt&0xFF, 8) + case 0x18: // seh + return SignExtend(rt&0xFFFF, 16) + case 0x02: // wsbh + return SignExtend(((rt&0xFF00FF00)>>8)|((rt&0x00FF00FF)<<8), 32) + } + case 0x03: // dext + assertMips64(insn) + lsb := (insn >> 6) & 0x1F + size := ((insn >> 11) & 0x1F) + 1 + mask := ((Word(1) << size) - 1) << lsb + return Word((rs & mask) >> lsb) + case 0x01: // dextm + assertMips64(insn) + lsb := (insn >> 6) & 0x1F + size := ((insn >> 11) & 0x1F) + 1 + 32 + mask := ((Word(1) << size) - 1) << lsb + return Word((rs & mask) >> lsb) + case 0x02: // dextu + assertMips64(insn) + lsb := (insn>>6)&0x1F + 32 + size := ((insn >> 11) & 0x1F) + 1 + mask := ((Word(1) << size) - 1) << lsb + return Word((rs & mask) >> lsb) + case 0x07: // dins + assertMips64(insn) + lsb := (insn >> 6) & 0x1F + size := ((insn >> 11) & 0x1F) + 1 - lsb + mask := (Word(1) << size) - 1 + return (rt & ^(mask << lsb)) | ((rs & mask) << lsb) + case 0x05: // dinsm + assertMips64(insn) + lsb := (insn >> 6) & 0x1F + size := ((insn >> 11) & 0x1F) + 1 + 32 - lsb + mask := (Word(1) << size) - 1 + return (rt & ^(mask << lsb)) | ((rs & mask) << lsb) + case 0x06: // dinsu + assertMips64(insn) + lsb := (insn>>6)&0x1F + 32 + size := ((insn >> 11) & 0x1F) + 1 + 32 - lsb + mask := (Word(1) << size) - 1 + return (rt & ^(mask << lsb)) | ((rs & mask) << lsb) + case 0x04: // ins + lsb := (insn >> 6) & 0x1F + size := ((insn >> 11) & 0x1F) + 1 - lsb + mask := (Word(1) << size) - 1 + return SignExtend(((rt & ^(mask << lsb)) | ((rs & mask) << lsb)), 32) + case 0x00: // ext + lsb := (insn >> 6) & 0x1F + size := ((insn >> 11) & 0x1F) + 1 + mask := (Word(1) << size) - 1 + return SignExtend((rs&(mask<>lsb, 32) + case 0x24: // dbshfl + assertMips64(insn) + switch (insn >> 6) & 0x1f { + case 0x02: // dsbh + return Word(((uint64(rt) & 0xFF00FF00FF00FF00) >> 8) | ((uint64(rt) & 0x00FF00FF00FF00FF) << 8)) + case 0x05: // dshd + return Word(((uint64(rt) & 0xFFFF) << 48) | (((uint64(rt) >> 16) & 0xFFFF) << 32) | (((uint64(rt) >> 32) & 0xFFFF) << 16) | ((uint64(rt) >> 48) & 0xFFFF)) + } + } default: panic(fmt.Sprintf("invalid instruction: %x", insn)) } @@ -526,7 +638,7 @@ func HandleBranch(cpu *mipsevm.CpuScalars, registers *[32]Word, opcode uint32, i } // 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 { +func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, insn, fun uint32, rs Word, rt Word, storeReg Word) error { val := Word(0) switch fun { case 0x10: // mfhi @@ -560,9 +672,13 @@ func HandleHiLo(cpu *mipsevm.CpuScalars, registers *[32]Word, fun uint32, rs Wor case 0x14: // dsllv assertMips64Fun(fun) val = rt << (rs & 0x3F) - case 0x16: // dsrlv + case 0x16: assertMips64Fun(fun) - val = rt >> (rs & 0x3F) + if (insn>>6)&0x1 == 1 { // drotrv + val = (rt >> (rs & 0x3F)) | (rt << (64 - (rs & 0x3F))) + } else { // dsrlv + val = rt >> (rs & 0x3F) + } case 0x17: // dsrav assertMips64Fun(fun) val = Word(int64(rt) >> (rs & 0x3F)) diff --git a/cannon/mipsevm/tests/evm_common64_test.go b/cannon/mipsevm/tests/evm_common64_test.go index 5c8d9b449ee82..942e007a60a1d 100644 --- a/cannon/mipsevm/tests/evm_common64_test.go +++ b/cannon/mipsevm/tests/evm_common64_test.go @@ -514,3 +514,527 @@ func TestEVM_SingleStep_Branch64(t *testing.T) { testBranch(t, cases) } + +func TestEVM_SingleStep_Clz64(t *testing.T) { + rsReg := uint32(7) + rdReg := uint32(8) + cases := []struct { + name string + rs Word + funct uint32 + expectedResult Word + }{ + // dclz + {name: "dclz", rs: Word(0x0), expectedResult: Word(64), funct: 0b10_0100}, + {name: "dclz", rs: Word(0x1), expectedResult: Word(63), funct: 0b10_0100}, + {name: "dclz", rs: Word(0x10_00_00_00), expectedResult: Word(35), funct: 0b10_0100}, + {name: "dclz", rs: Word(0x80_00_00_00), expectedResult: Word(32), funct: 0b10_0100}, + {name: "dclz", rs: Word(0x80_00_00_00_00_00_00_00), expectedResult: Word(0), funct: 0b10_0100}, + {name: "dclz, sign-extended", rs: Word(0x80_00_00_00_00_00_00_00), expectedResult: Word(0), funct: 0b10_0100}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + insn := 0b01_1100<<26 | rsReg<<21 | rdReg<<11 | tt.funct + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rsReg] = tt.rs + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rdReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Rot64(t *testing.T) { + t.Parallel() + rsReg := uint32(7) + rdReg := uint32(8) + rtReg := uint32(9) + + cases := []struct { + name string + rs Word + rt Word + sa uint32 + funct uint32 + expectedResult Word + }{ + // drotr + // 0x2 rotated right by 1 -> 0x1 + {name: "drotr", sa: 1, rt: Word(0x2), expectedResult: Word(0x1), funct: 0b11_1010}, + // 0x1 rotated right by 1 -> MSB set + {name: "drotr MSB set", sa: 1, rt: Word(0x1), expectedResult: Word(0x80_00_00_00_00_00_00_00), funct: 0b11_1010}, + // Rotate right by 8 (byte-level rotation) + {name: "drotr byte shift", sa: 8, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0xF0123456789ABCDE), funct: 0b11_1010}, + // Rotate right by 16 (halfword-level rotation) + {name: "drotr halfword shift", sa: 16, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0xDEF0123456789ABC), funct: 0b11_1010}, + // Edge case: rotating zero + {name: "drotr zero", sa: 5, rt: Word(0x0), expectedResult: Word(0x0), funct: 0b11_1010}, + + // drotr32 + // Rotate by exactly 32 bits (should swap halves) + {name: "drotr32", sa: 32 + 0, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x9ABCDEF012345678), funct: 0b11_1110}, + // Rotate by 33 bits (should shift one more bit beyond simple case) + {name: "drotr32 by 1", sa: 32 + 1, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x4d5e6f78091a2b3c), funct: 0b11_1110}, + // Rotate by 40 bits (byte-level rotation) + {name: "drotr32 by 8 (byte shift)", sa: 32 + 8, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x789abcdef0123456), funct: 0b11_1110}, + // Rotate by 48 bits (halfword-level rotation) + {name: "drotr32 by 16 (halfword shift)", sa: 32 + 16, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x56789abcdef01234), funct: 0b11_1110}, + // Rotate by 63 bits (one less than full 64-bit cycle) + {name: "drotr32 by 31", sa: 32 + 31, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x2468acf13579bde0), funct: 0b11_1110}, + // Rotate with MSB set, shifting it down into the lower bits + {name: "drotr32 with MSB set", sa: 32 + 4, rt: Word(0x8000000000000000), expectedResult: Word(0x0000000008000000), funct: 0b11_1110}, + // Rotate all ones (0xFFFFFFFFFFFFFFFF) should remain unchanged + {name: "drotr32 all ones", sa: 32 + 8, rt: Word(0xFFFFFFFFFFFFFFFF), expectedResult: Word(0xFFFFFFFFFFFFFFFF), funct: 0b11_1110}, + // Rotate zero (should remain zero) + {name: "drotr32 zero", sa: 32 + 5, rt: Word(0x0000000000000000), expectedResult: Word(0x0000000000000000), funct: 0b11_1110}, + + // drotrv + // 0x2 rotated right by 1 -> 0x1 + {name: "drotrv", rs: 1, rt: Word(0x2), expectedResult: Word(0x1), funct: 0b01_0110}, + // 0x1 rotated right by 1 -> MSB set + {name: "drotrv MSB set", rs: 1, rt: Word(0x1), expectedResult: Word(0x80_00_00_00_00_00_00_00), funct: 0b01_0110}, + // Rotate right by 8 (byte-level rotation) + {name: "drotrv byte shift", rs: 8, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0xF0123456789ABCDE), funct: 0b01_0110}, + // Rotate right by 16 (halfword-level rotation) + {name: "drotrv halfword shift", rs: 16, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0xDEF0123456789ABC), funct: 0b01_0110}, + // Rotate by exactly 32 bits (should swap halves) + {name: "drotrv by 32", rs: 32, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x9ABCDEF012345678), funct: 0b01_0110}, + // Rotate by 33 bits (should shift one more bit beyond simple case) + {name: "drotrv by 33", rs: 33, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x4d5e6f78091a2b3c), funct: 0b01_0110}, + // Rotate by 40 bits (byte-level rotation) + {name: "drotrv by 40", rs: 40, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x789abcdef0123456), funct: 0b01_0110}, + // Rotate by 48 bits (halfword-level rotation) + {name: "drotrv by 48", rs: 48, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x56789abcdef01234), funct: 0b01_0110}, + // Rotate by 63 bits (one less than full 64-bit cycle) + {name: "drotrv by 63", rs: 63, rt: Word(0x123456789ABCDEF0), expectedResult: Word(0x2468acf13579bde0), funct: 0b01_0110}, + // Rotate with MSB set, shifting it down into the lower bits + {name: "drotrv with MSB set", rs: 36, rt: Word(0x8000000000000000), expectedResult: Word(0x0000000008000000), funct: 0b01_0110}, + // Rotate all ones (0xFFFFFFFFFFFFFFFF) should remain unchanged + {name: "drotrv all ones", rs: 40, rt: Word(0xFFFFFFFFFFFFFFFF), expectedResult: Word(0xFFFFFFFFFFFFFFFF), funct: 0b01_0110}, + // Rotate zero (should remain zero) + {name: "drotrv zero", rs: 5, rt: Word(0x0), expectedResult: Word(0x0), funct: 0b01_0110}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + if tt.funct == 0b11_1010 { // drotr + insn = 1<<21 | rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct + } else if tt.funct == 0b11_1110 { // drotr32 + require.GreaterOrEqual(t, tt.sa, uint32(32), "sa should be >= 32 for drotr32") + insn = 1<<21 | rtReg<<16 | rdReg<<11 | (tt.sa-32)<<6 | tt.funct + } else if tt.funct == 0b01_0110 { // drotrv + insn = rsReg<<21 | rtReg<<16 | rdReg<<11 | 1<<6 | tt.funct + } + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rtReg] = tt.rt + if tt.funct == 0b01_0110 { // drotrv + state.GetRegistersRef()[rsReg] = tt.rs + } + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rdReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Ext64(t *testing.T) { + t.Parallel() + rsReg := uint32(7) + rtReg := uint32(9) + + cases := []struct { + name string + rs Word + msbd uint32 + lsb uint32 + funct uint32 + expectedResult Word + }{ + // dext + // Extract 8 bits starting at bit 0 (byte 0) + {name: "dext byte 0", rs: Word(0x123456789ABCDEF0), msbd: 8 - 1, lsb: 0, funct: 0b000011, expectedResult: Word(0xF0)}, + // Extract 8 bits starting at bit 8 (byte 1) + {name: "dext byte 1", rs: Word(0x123456789ABCDEF0), msbd: 8 - 1, lsb: 8, funct: 0b000011, expectedResult: Word(0xDE)}, + // Extract 8 bits starting at bit 16 (byte 2) + {name: "dext byte 2", rs: Word(0x123456789ABCDEF0), msbd: 8 - 1, lsb: 16, funct: 0b000011, expectedResult: Word(0xBC)}, + // Extract 8 bits starting at bit 24 (byte 3) + {name: "dext byte 3", rs: Word(0x123456789ABCDEF0), msbd: 8 - 1, lsb: 24, funct: 0b000011, expectedResult: Word(0x9A)}, + // Extract 4 bits starting at bit 0 (low nibble) + {name: "dext nibble low", rs: Word(0x123456789ABCDEF0), msbd: 4 - 1, lsb: 0, funct: 0b000011, expectedResult: Word(0x0)}, + // Extract 4 bits starting at bit 12 (high nibble) + {name: "dext nibble high", rs: Word(0x123456789ABCDEF0), msbd: 4 - 1, lsb: 12, funct: 0b000011, expectedResult: Word(0xD)}, + // Extract full 16-bit halfword [15:0] + {name: "dext half word", rs: Word(0x123456789ABCDEF0), msbd: 16 - 1, lsb: 0, funct: 0b000011, expectedResult: Word(0xDEF0)}, + // Extract full 32-bit word [31:0] + {name: "dext full word", rs: Word(0x123456789ABCDEF0), msbd: 32 - 1, lsb: 0, funct: 0b000011, expectedResult: Word(0x9ABCDEF0)}, + + // dextm + // Extract 32 + 8 bits starting at bit 20 [60:20] + {name: "dextm by 40", rs: Word(0x123456789ABCDEF0), msbd: 32 + (8 - 1), lsb: 20, funct: 0b000001, expectedResult: Word(0x23456789AB)}, + // Extract 32 + 16 bits starting at bit 24 [48:0] + {name: "dextm by 48", rs: Word(0x123456789ABCDEF0), msbd: 32 + (16 - 1), lsb: 0, funct: 0b000001, expectedResult: Word(0x56789ABCDEF0)}, + // Extract 32 + 28 word from bit 4 [63:4] + {name: "dextm by 60", rs: Word(0x123456789ABCDEF0), msbd: 32 + (28 - 1), lsb: 4, funct: 0b000001, expectedResult: Word(0x123456789ABCDEF)}, + + // dextu + // Extract 4 bits from bit 40 + {name: "dextu byte 5", rs: Word(0x123456789ABCDEF0), msbd: 4 - 1, lsb: 40, funct: 0b000010, expectedResult: Word(0x6)}, + // Extract 12 bits from bit 44 + {name: "dextu 12-bit unaligned", rs: Word(0x123456789ABCDEF0), msbd: 12 - 1, lsb: 44, funct: 0b000010, expectedResult: Word(0x345)}, + // Extract 16 bits from bit 48 (halfword in upper half) + {name: "dextu halfword", rs: Word(0x123456789ABCDEF0), msbd: 16 - 1, lsb: 48, funct: 0b000010, expectedResult: Word(0x1234)}, + // Extract 24 bits from bit 36 + {name: "dextu 24-bit field", rs: Word(0x123456789ABCDEF0), msbd: 24 - 1, lsb: 36, funct: 0b000010, expectedResult: Word(0x234567)}, + // Extract full 32-bit word from bit 32 + {name: "dextu full word", rs: Word(0x123456789ABCDEF0), msbd: 32 - 1, lsb: 32, funct: 0b000010, expectedResult: Word(0x12345678)}, + + // ext + // Extract lower 8 bits (byte 0) + {name: "ext byte 0", rs: Word(0x12345678), msbd: 8 - 1, lsb: 0, funct: 0b000000, expectedResult: Word(0x78)}, + // Extract bits 8-15 (byte 1) + {name: "ext byte 1", rs: Word(0x12345678), msbd: 8 - 1, lsb: 8, funct: 0b000000, expectedResult: Word(0x56)}, + // Extract bits 16-23 (byte 2) + {name: "ext byte 2", rs: Word(0x12345678), msbd: 8 - 1, lsb: 16, funct: 0b000000, expectedResult: Word(0x34)}, + // Extract bits 24-31 (byte 3) + {name: "ext byte 3", rs: Word(0x12345678), msbd: 8 - 1, lsb: 24, funct: 0b000000, expectedResult: Word(0x12)}, + // Extract 16-bit halfword from bits 8-23 + {name: "ext halfword", rs: Word(0x12345678), msbd: 16 - 1, lsb: 8, funct: 0b000000, expectedResult: Word(0x3456)}, + // Extract full 32-bit word (should return the same value) + {name: "ext full word", rs: Word(0x12345678), msbd: 32 - 1, lsb: 0, funct: 0b000000, expectedResult: Word(0x12345678)}, + // Extract full 32-bit word sign extended + {name: "ext full word", rs: Word(0xFFFFFFFF), msbd: 32 - 1, lsb: 0, funct: 0b000000, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + if tt.funct == 0b00_0011 || tt.funct == 0b00_0000 { // dext, ext + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | tt.msbd<<11 | tt.lsb<<6 | tt.funct + } else if tt.funct == 0b00_0001 { // dextm + require.GreaterOrEqual(t, tt.msbd, uint32(32), "msbd should be >= 32 for dextm") + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | (tt.msbd-32)<<11 | tt.lsb<<6 | tt.funct + } else if tt.funct == 0b00_0010 { // dextu + require.GreaterOrEqual(t, tt.lsb, uint32(32), "lsb should be >= 32 for dextu") + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | tt.msbd<<11 | (tt.lsb-32)<<6 | tt.funct + } + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rsReg] = tt.rs + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rtReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Ins64(t *testing.T) { + t.Parallel() + rsReg := uint32(7) + rtReg := uint32(9) + + cases := []struct { + name string + rs Word + rt Word + msb uint32 + lsb uint32 + funct uint32 + expectedResult Word + }{ + // dins + // Insert 8 bits from rs into rt at bit 16 + {name: "dins byte insert", rs: Word(0x12345678), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 16 + (8 - 1), lsb: 16, funct: 0b000111, expectedResult: Word(0xFFFFFFFFFF_78_FFFF)}, + // Insert 12 bits from rs into rt at bit 20 + {name: "dins 12-bit insert", rs: Word(0x12345678), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 20 + (12 - 1), lsb: 20, funct: 0b000111, expectedResult: Word(0xFFFFFFFF_678_FFFFF)}, + // Insert 16 bits from rs into rt at bit 8 + {name: "dins halfword insert", rs: Word(0x12345678), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 8 + (16 - 1), lsb: 8, funct: 0b000111, expectedResult: Word(0xFFFFFFFFFF_5678_FF)}, + // Insert 24 bits from rs into rt at bit 4 + {name: "dins 24-bit insert", rs: Word(0x12345678), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 4 + (24 - 1), lsb: 4, funct: 0b000111, expectedResult: Word(0xFFFFFFFFF_345678_F)}, + // Insert 32 bits from rs into rt at bit 0 + {name: "dins full word insert", rs: Word(0x12345678), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 0 + (32 - 1), lsb: 0, funct: 0b000111, expectedResult: Word(0xFFFFFFFF_12345678)}, + + // dinsm + // Insert 32 + 8 bits from rs into rt at bit 24 + {name: "dinsm byte insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 32 + (8 - 1), lsb: 24, funct: 0b000101, expectedResult: Word(0xFFFFFF_DEF0_FFFFFF)}, + // Insert 32 + 12 bits from rs into rt at bit 28 + {name: "dinsm 12-bit insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 32 + (12 - 1), lsb: 28, funct: 0b000101, expectedResult: Word(0xFFFFF_DEF0_FFFFFFF)}, + // Insert 32 + 16 bits from rs into rt at bit 20 + {name: "dinsm halfword insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 32 + (16 - 1), lsb: 20, funct: 0b000101, expectedResult: Word(0xFFFF_ABCDEF0_FFFFF)}, + // Insert 32 + 24 bits from rs into rt at bit 12 + {name: "dinsm 24-bit insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 32 + (24 - 1), lsb: 12, funct: 0b000101, expectedResult: Word(0xFF_6789ABCDEF0_FFF)}, + // Insert 32 + 32 bits from rs into rt at bit 0 + {name: "dinsm full dword insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 32 + (32 - 1), lsb: 0, funct: 0b000101, expectedResult: Word(0x123456789ABCDEF0)}, + + // dinsu + // Insert 8 bits from rs into rt at bit 40 + {name: "dinsu byte insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 40 + (8 - 1), lsb: 40, funct: 0b000110, expectedResult: Word(0xFFFF_F0_FFFFFFFFFF)}, + // Insert 12 bits from rs into rt at bit 44 + {name: "dinsu 12-bit insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 44 + (12 - 1), lsb: 44, funct: 0b000110, expectedResult: Word(0xFF_EF0_FFFFFFFFFFF)}, + // Insert 16 bits from rs into rt at bit 48 + {name: "dinsu halfword insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 48 + (16 - 1), lsb: 48, funct: 0b000110, expectedResult: Word(0xDEF0_FFFFFFFFFFFF)}, + // Insert 24 bits from rs into rt at bit 36 + {name: "dinsu 24-bit insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 36 + (24 - 1), lsb: 36, funct: 0b000110, expectedResult: Word(0xF_BCDEF0_FFFFFFFFF)}, + // Insert 32 bits from rs into rt at bit 32 + {name: "dinsu full word insert", rs: Word(0x123456789ABCDEF0), rt: Word(0xFFFFFFFFFFFFFFFF), msb: 32 + (32 - 1), lsb: 32, funct: 0b000110, expectedResult: Word(0x9ABCDEF0_FFFFFFFF)}, + + // ins + // Insert 8-bit value from rs into rt at bit 0 + {name: "ins byte 0", rs: Word(0x000000AA), rt: Word(0x0FFF0000), msb: 0 + (8 - 1), lsb: 0, funct: 0b000100, expectedResult: Word(0x0FFF00AA)}, + // Insert 8-bit value from rs into rt at bit 8 + {name: "ins byte 1", rs: Word(0x000000AA), rt: Word(0x0FFF0000), msb: 8 + (8 - 1), lsb: 8, funct: 0b000100, expectedResult: Word(0x0FFFAA00)}, + // Insert 16-bit value from rs into rt at bit 0 + {name: "ins halfword 0", rs: Word(0x0000AAAA), rt: Word(0x0FFF0000), msb: 0 + (16 - 1), lsb: 0, funct: 0b000100, expectedResult: Word(0x0FFFAAAA)}, + // Insert 16-bit value from rs into rt at bit 8 (sign extend) + {name: "ins halfword 1", rs: Word(0x0000AAAA), rt: Word(0xFFFF0000), msb: 8 + (16 - 1), lsb: 8, funct: 0b000100, expectedResult: Word(0xFFFFFFFFFFAAAA00)}, + // Insert 24-bit value from rs into rt at bit 4 (sign extend) + {name: "ins 24-bit", rs: Word(0x00AAAAAA), rt: Word(0xFFFF0000), msb: 4 + (24 - 1), lsb: 4, funct: 0b000100, expectedResult: Word(0xFFFFFFFFFAAAAAA0)}, + // Insert full 32-bit value from rs into rt (sign extend) + {name: "ins full word", rs: Word(0xAAAAAAAA), rt: Word(0xFFFF0000), msb: 0 + (32 - 1), lsb: 0, funct: 0b000100, expectedResult: Word(0xFFFFFFFFAAAAAAAA)}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + if tt.funct == 0b00_0111 || tt.funct == 0b00_0100 { // dins, ins + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | tt.msb<<11 | tt.lsb<<6 | tt.funct + } else if tt.funct == 0b00_0101 { // dinsm + require.GreaterOrEqual(t, tt.msb, uint32(32), "msb should be >= 32 for dextm") + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | (tt.msb-32)<<11 | tt.lsb<<6 | tt.funct + } else if tt.funct == 0b00_0110 { // dinsu + require.GreaterOrEqual(t, tt.msb, uint32(32), "msb should be >= 32 for dextm") + require.GreaterOrEqual(t, tt.lsb, uint32(32), "lsb should be >= 32 for dextu") + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | (tt.msb-32)<<11 | (tt.lsb-32)<<6 | tt.funct + } + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rtReg] = tt.rt + state.GetRegistersRef()[rsReg] = tt.rs + + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rtReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Swap64(t *testing.T) { + t.Parallel() + rdReg := uint32(8) + rtReg := uint32(9) + + cases := []struct { + name string + rt Word + funct uint32 + special uint32 + expectedResult Word + }{ + // dsbh + // Swap bytes within halfwords for a 64-bit value + {name: "dsbh", rt: Word(0x1122334455667788), funct: 0b100100, special: 0b00010, expectedResult: Word(0x2211443366558877)}, + // Swap bytes within halfwords when all bytes are the same + {name: "dsbh all same", rt: Word(0xFFFFFFFFFFFFFFFF), funct: 0b100100, special: 0b00010, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + // Swap bytes within halfwords with zeros + {name: "dsbh with zero", rt: Word(0x0000FFFF0000FFFF), funct: 0b100100, special: 0b00010, expectedResult: Word(0x0000FFFF0000FFFF)}, + // Swap bytes within halfwords when all bytes are different + {name: "dsbh all different", rt: Word(0x123456789ABCDEF0), funct: 0b100100, special: 0b00010, expectedResult: Word(0x34127856BC9AF0DE)}, + + // dshd + // Swap halfwords within doubleword + {name: "dshd", rt: Word(0x1122334455667788), funct: 0b100100, special: 0b00101, expectedResult: Word(0x7788556633441122)}, + // Swap halfwords within doubleword with alternating bit patterns + {name: "dshd pattern", rt: Word(0xAABBCCDDEEFF0011), funct: 0b100100, special: 0b00101, expectedResult: Word(0x0011EEFFCCDDAABB)}, + // Swap halfwords within doubleword when all bytes are the same + {name: "dshd all same", rt: Word(0xFFFFFFFFFFFFFFFF), funct: 0b100100, special: 0b00101, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + // Swap halfwords within doubleword with zeros + {name: "dshd with zero", rt: Word(0x0000FFFF0000FFFF), funct: 0b100100, special: 0b00101, expectedResult: Word(0xFFFF0000FFFF0000)}, + // Swap halfwords within doubleword when all bytes are different + {name: "dshd half reversed", rt: Word(0x123456789ABCDEF0), funct: 0b100100, special: 0b00101, expectedResult: Word(0xDEF09ABC56781234)}, + + // wsbh + // Swap bytes within halfwords (lower 32 bits) + {name: "wsbh", rt: Word(0x11223344), funct: 0b100000, special: 0b00010, expectedResult: Word(0x0000000022114433)}, + // Swap bytes within halfwords (sign extend) + {name: "wsbh sign extend", rt: Word(0xEEFF0011), funct: 0b100000, special: 0b00010, expectedResult: Word(0xFFFFFFFFFFEE1100)}, + // Swap bytes within halfwords (all bits set) + {name: "wsbh all ones 64-bit", rt: Word(0xFFFFFFFF), funct: 0b100000, special: 0b00010, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + // Swap bytes within halfwords (all bits zero) + {name: "wsbh all zero 64-bit", rt: Word(0x00000000), funct: 0b100000, special: 0b00010, expectedResult: Word(0x0000000000000000)}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + insn = 0b011111<<26 | rtReg<<16 | rdReg<<11 | tt.special<<6 | tt.funct + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rtReg] = tt.rt + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rdReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_SignExtend64(t *testing.T) { + t.Parallel() + rdReg := uint32(8) + rtReg := uint32(9) + cases := []struct { + name string + rt Word + funct uint32 + special uint32 + expectedResult Word + }{ + // seb + // Sign-extend byte (positive value) + {name: "seb positive", rt: Word(0x0000007F), funct: 0b100000, special: 0b10000, expectedResult: Word(0x000000000000007F)}, + // Sign-extend byte (negative value) + {name: "seb negative", rt: Word(0x00000080), funct: 0b100000, special: 0b10000, expectedResult: Word(0xFFFFFFFFFFFFFF80)}, + // Sign-extend byte (mid-range) + {name: "seb mid-range", rt: Word(0x00000055), funct: 0b100000, special: 0b10000, expectedResult: Word(0x0000000000000055)}, + // Sign-extend byte (full 8-bit set) + {name: "seb full-byte", rt: Word(0x000000FF), funct: 0b100000, special: 0b10000, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + // Sign-extend byte with upper bits set + {name: "seb upper bits", rt: Word(0x123456FF), funct: 0b100000, special: 0b10000, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + + // seh + {name: "seh positive", rt: Word(0x00007FFF), funct: 0b100000, special: 0b11000, expectedResult: Word(0x0000000000007FFF)}, + // Sign-extend halfword (negative value) + {name: "seh negative", rt: Word(0x00008000), funct: 0b100000, special: 0b11000, expectedResult: Word(0xFFFFFFFFFFFF8000)}, + // Sign-extend halfword (mid-range) + {name: "seh mid-range", rt: Word(0x00005555), funct: 0b100000, special: 0b11000, expectedResult: Word(0x0000000000005555)}, + // Sign-extend halfword (full 16-bit set) + {name: "seh full-halfword", rt: Word(0x0000FFFF), funct: 0b100000, special: 0b11000, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + // Sign-extend halfword with upper bits set + {name: "seh upper bits", rt: Word(0x1234FFFF), funct: 0b100000, special: 0b11000, expectedResult: Word(0xFFFFFFFFFFFFFFFF)}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + insn = 0b011111<<26 | rtReg<<16 | rdReg<<11 | tt.special<<6 | tt.funct + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rtReg] = tt.rt + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rdReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} diff --git a/cannon/mipsevm/tests/evm_common_test.go b/cannon/mipsevm/tests/evm_common_test.go index c43fb19e237ac..b1c28b3d0bf7b 100644 --- a/cannon/mipsevm/tests/evm_common_test.go +++ b/cannon/mipsevm/tests/evm_common_test.go @@ -1155,3 +1155,351 @@ func TestEVM_SingleStep_Branch32(t *testing.T) { testBranch(t, cases) } + +func TestEVM_SingleStep_Rot(t *testing.T) { + t.Parallel() + rsReg := uint32(7) + rdReg := uint32(8) + rtReg := uint32(9) + versions := GetMipsVersionTestCases(t) + cases := []struct { + name string + rs Word + rt Word + sa uint32 + funct uint32 + expectVal Word + }{ + // rotr + // No rotation (should return the same number) + {name: "rotr no shift", rt: Word(0x12345678), sa: 0, funct: 0b000010, expectVal: signExtend64(Word(0x12345678))}, + // Rotate by 1 bit + {name: "rotr by 1", rt: Word(0x12345678), sa: 1, funct: 0b000010, expectVal: signExtend64(Word(0x091A2B3C))}, + // Rotate by 4 bits (nibble rotation) + {name: "rotr by 4", rt: Word(0x12345678), sa: 4, funct: 0b000010, expectVal: signExtend64(Word(0x81234567))}, + // Rotate by 8 bits (byte rotation) + {name: "rotr by 8", rt: Word(0x12345678), sa: 8, funct: 0b000010, expectVal: signExtend64(Word(0x78123456))}, + // Rotate by 16 bits (half-word rotation) + {name: "rotr by 16", rt: Word(0x12345678), sa: 16, funct: 0b000010, expectVal: signExtend64(Word(0x56781234))}, + // Rotate by 24 bits + {name: "rotr by 24", rt: Word(0x12345678), sa: 24, funct: 0b000010, expectVal: signExtend64(Word(0x34567812))}, + // Rotate by 31 bits (almost a full cycle) + {name: "rotr by 31", rt: Word(0x12345678), sa: 31, funct: 0b000010, expectVal: signExtend64(Word(0x2468ACF0))}, + // Rotate with MSB set + {name: "rotr MSB set", rt: Word(0x80000000), sa: 4, funct: 0b000010, expectVal: signExtend64(Word(0x08000000))}, + // Rotate all ones (0xFFFFFFFF) should remain unchanged + {name: "rotr all ones", rt: Word(0xFFFFFFFF), sa: 8, funct: 0b000010, expectVal: signExtend64(Word(0xFFFFFFFF))}, + // Rotate zero (should remain zero) + {name: "rotr zero", rt: Word(0x00000000), sa: 5, funct: 0b000010, expectVal: signExtend64(Word(0x00000000))}, + + // rotrv + // No rotation (should return the same number) + {name: "rotrv no shift", rt: Word(0x12345678), rs: Word(0), funct: 0b000110, expectVal: signExtend64(Word(0x12345678))}, + // Rotate by 1 bit + {name: "rotrv by 1", rt: Word(0x12345678), rs: Word(1), funct: 0b000110, expectVal: signExtend64(Word(0x091A2B3C))}, + // Rotate by 4 bits (nibble rotation) + {name: "rotrv by 4", rt: Word(0x12345678), rs: Word(4), funct: 0b000110, expectVal: signExtend64(Word(0x81234567))}, + // Rotate by 8 bits (byte rotation) + {name: "rotrv by 8", rt: Word(0x12345678), rs: Word(8), funct: 0b000110, expectVal: signExtend64(Word(0x78123456))}, + // Rotate by 16 bits (half-word rotation) + {name: "rotrv by 16", rt: Word(0x12345678), rs: Word(16), funct: 0b000110, expectVal: signExtend64(Word(0x56781234))}, + // Rotate by 24 bits + {name: "rotrv by 24", rt: Word(0x12345678), rs: Word(24), funct: 0b000110, expectVal: signExtend64(Word(0x34567812))}, + // Rotate by 31 bits (almost a full cycle) + {name: "rotrv by 31", rt: Word(0x12345678), rs: Word(31), funct: 0b000110, expectVal: signExtend64(Word(0x2468ACF0))}, + // Rotate with MSB set + {name: "rotrv MSB set", rt: Word(0x80000000), rs: Word(4), funct: 0b000110, expectVal: signExtend64(Word(0x08000000))}, + // Rotate all ones (0xFFFFFFFF) should remain unchanged + {name: "rotrv all ones", rt: Word(0xFFFFFFFF), rs: Word(8), funct: 0b000110, expectVal: signExtend64(Word(0xFFFFFFFF))}, + // Rotate zero (should remain zero) + {name: "rotrv zero", rt: Word(0x00000000), rs: Word(5), funct: 0b000110, expectVal: signExtend64(Word(0x00000000))}, + } + + 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.WithPC(0), testutil.WithNextPC(4)) + state := goVm.GetState() + var insn uint32 + if tt.funct == 0b00_0010 { // rotr + insn = 1<<21 | rtReg<<16 | rdReg<<11 | tt.sa<<6 | tt.funct + } else if tt.funct == 0b00_0110 { // rotrv + insn = rsReg<<21 | rtReg<<16 | rdReg<<11 | 1<<6 | tt.funct + } + state.GetRegistersRef()[rtReg] = tt.rt + if tt.funct == 0b00_0110 { // rotrv + state.GetRegistersRef()[rsReg] = tt.rs + } + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + //step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + + expected.Registers[rdReg] = tt.expectVal + + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_SignExtend(t *testing.T) { + t.Parallel() + rdReg := uint32(8) + rtReg := uint32(9) + versions := GetMipsVersionTestCases(t) + cases := []struct { + name string + rt Word + funct uint32 + special uint32 + expectVal Word + }{ + // seb + // Sign-extend byte (positive value) + {name: "seb positive", rt: Word(0x0000007F), funct: 0b100000, special: 0b10000, expectVal: signExtend64(Word(0x0000007F))}, + // Sign-extend byte (negative value) + {name: "seb negative", rt: Word(0x00000080), funct: 0b100000, special: 0b10000, expectVal: signExtend64(Word(0xFFFFFF80))}, + // Sign-extend byte (mid-range) + {name: "seb mid-range", rt: Word(0x00000055), funct: 0b100000, special: 0b10000, expectVal: signExtend64(Word(0x00000055))}, + // Sign-extend byte (full 8-bit set) + {name: "seb full-byte", rt: Word(0x000000FF), funct: 0b100000, special: 0b10000, expectVal: signExtend64(Word(0xFFFFFFFF))}, + // Sign-extend byte with upper bits set + {name: "seb upper bits", rt: Word(0x123456FF), funct: 0b100000, special: 0b10000, expectVal: signExtend64(Word(0xFFFFFFFF))}, + + // seh + // Sign-extend halfword (positive value) + {name: "seh positive", rt: Word(0x00007FFF), funct: 0b100000, special: 0b11000, expectVal: signExtend64(Word(0x00007FFF))}, + // Sign-extend halfword (negative value) + {name: "seh negative", rt: Word(0x00008000), funct: 0b100000, special: 0b11000, expectVal: signExtend64(Word(0xFFFF8000))}, + // Sign-extend halfword (mid-range) + {name: "seh mid-range", rt: Word(0x00005555), funct: 0b100000, special: 0b11000, expectVal: signExtend64(Word(0x00005555))}, + // Sign-extend halfword (full 16-bit set) + {name: "seh full-halfword", rt: Word(0x0000FFFF), funct: 0b100000, special: 0b11000, expectVal: signExtend64(Word(0xFFFFFFFF))}, + // Sign-extend halfword with upper bits set + {name: "seh upper bits", rt: Word(0x1234FFFF), funct: 0b100000, special: 0b11000, expectVal: signExtend64(Word(0xFFFFFFFF))}, + } + + 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.WithPC(0), testutil.WithNextPC(4)) + state := goVm.GetState() + var insn uint32 + insn = 0b011111<<26 | rtReg<<16 | rdReg<<11 | tt.special<<6 | tt.funct + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rtReg] = tt.rt + //step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + + expected.Registers[rdReg] = tt.expectVal + + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Swap(t *testing.T) { + t.Parallel() + rdReg := uint32(8) + rtReg := uint32(9) + + cases := []struct { + name string + rt Word + funct uint32 + special uint32 + expectedResult Word + }{ + // wsbh + // Swap bytes within halfwords (standard case) + {name: "wsbh", rt: Word(0x11223344), funct: 0b100000, special: 0b00010, expectedResult: signExtend64(Word(0x22114433))}, + // Swap bytes within halfwords (alternating pattern) + {name: "wsbh pattern", rt: Word(0xAABBCCDD), funct: 0b100000, special: 0b00010, expectedResult: signExtend64(Word(0xBBAADDCC))}, + // Swap bytes within halfwords (all bits set) + {name: "wsbh all ones", rt: Word(0xFFFFFFFF), funct: 0b100000, special: 0b00010, expectedResult: signExtend64(Word(0xFFFFFFFF))}, + // Swap bytes within halfwords (all bits zero) + {name: "wsbh all zero", rt: Word(0x00000000), funct: 0b100000, special: 0b00010, expectedResult: signExtend64(Word(0x00000000))}, + // Swap bytes within halfwords (mixed values) + {name: "wsbh mixed", rt: Word(0x12345678), funct: 0b100000, special: 0b00010, expectedResult: signExtend64(Word(0x34127856))}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + insn = 0b011111<<26 | rtReg<<16 | rdReg<<11 | tt.special<<6 | tt.funct + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rtReg] = tt.rt + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rdReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Ext(t *testing.T) { + t.Parallel() + rsReg := uint32(7) + rtReg := uint32(9) + + cases := []struct { + name string + rs Word + msbd uint32 + lsb uint32 + funct uint32 + expectedResult Word + }{ + // ext + // Extract lower 8 bits (byte 0) + {name: "ext byte 0", rs: Word(0x12345678), msbd: 8 - 1, lsb: 0, funct: 0b000000, expectedResult: signExtend64(Word(0x78))}, + // Extract bits 8-15 (byte 1) + {name: "ext byte 1", rs: Word(0x12345678), msbd: 8 - 1, lsb: 8, funct: 0b000000, expectedResult: signExtend64(Word(0x56))}, + // Extract bits 16-23 (byte 2) + {name: "ext byte 2", rs: Word(0x12345678), msbd: 8 - 1, lsb: 16, funct: 0b000000, expectedResult: signExtend64(Word(0x34))}, + // Extract bits 24-31 (byte 3) + {name: "ext byte 3", rs: Word(0x12345678), msbd: 8 - 1, lsb: 24, funct: 0b000000, expectedResult: signExtend64(Word(0x12))}, + // Extract 16-bit halfword from bits 8-23 + {name: "ext halfword", rs: Word(0x12345678), msbd: 16 - 1, lsb: 8, funct: 0b000000, expectedResult: signExtend64(Word(0x3456))}, + // Extract full 32-bit word (should return the same value) + {name: "ext full word", rs: Word(0x12345678), msbd: 32 - 1, lsb: 0, funct: 0b000000, expectedResult: signExtend64(Word(0x12345678))}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | tt.msbd<<11 | tt.lsb<<6 | tt.funct + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rsReg] = tt.rs + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rtReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +} + +func TestEVM_SingleStep_Ins(t *testing.T) { + t.Parallel() + rsReg := uint32(7) + rtReg := uint32(9) + + cases := []struct { + name string + rs Word + rt Word + msb uint32 + lsb uint32 + funct uint32 + expectedResult Word + }{ + // ins + // Insert 8-bit value from rs into rt at bit 0 + {name: "ins byte 0", rs: Word(0x000000AA), rt: Word(0xFFFF0000), msb: 0 + (8 - 1), lsb: 0, funct: 0b000100, expectedResult: signExtend64(Word(0xFFFF00AA))}, + // Insert 8-bit value from rs into rt at bit 8 + {name: "ins byte 1", rs: Word(0x000000AA), rt: Word(0xFFFF0000), msb: 8 + (8 - 1), lsb: 8, funct: 0b000100, expectedResult: signExtend64(Word(0xFFFFAA00))}, + // Insert 16-bit value from rs into rt at bit 0 + {name: "ins halfword 0", rs: Word(0x0000AAAA), rt: Word(0xFFFF0000), msb: 0 + (16 - 1), lsb: 0, funct: 0b000100, expectedResult: signExtend64(Word(0xFFFFAAAA))}, + // Insert 16-bit value from rs into rt at bit 8 + {name: "ins halfword 1", rs: Word(0x0000AAAA), rt: Word(0xFFFF0000), msb: 8 + (16 - 1), lsb: 8, funct: 0b000100, expectedResult: signExtend64(Word(0xFFAAAA00))}, + // Insert 24-bit value from rs into rt at bit 4 + {name: "ins 24-bit", rs: Word(0x00AAAAAA), rt: Word(0xFFFF0000), msb: 4 + (24 - 1), lsb: 4, funct: 0b000100, expectedResult: signExtend64(Word(0xFAAAAAA0))}, + // Insert full 32-bit value from rs into rt + {name: "ins full word", rs: Word(0xAAAAAAAA), rt: Word(0xFFFF0000), msb: 0 + (32 - 1), lsb: 0, funct: 0b000100, expectedResult: signExtend64(Word(0xAAAAAAAA))}, + } + + versions := GetMipsVersionTestCases(t) + for _, v := range versions { + for i, tt := range cases { + testName := fmt.Sprintf("%v (%v)", tt.name, v.Name) + t.Run(testName, func(t *testing.T) { + // Set up state + goVm := v.VMFactory(nil, os.Stdout, os.Stderr, testutil.CreateLogger(), testutil.WithRandomization(int64(i))) + state := goVm.GetState() + + var insn uint32 + insn = 0b011111<<26 | rsReg<<21 | rtReg<<16 | tt.msb<<11 | tt.lsb<<6 | tt.funct + + testutil.StoreInstruction(state.GetMemory(), state.GetPC(), insn) + state.GetRegistersRef()[rsReg] = tt.rs + state.GetRegistersRef()[rtReg] = tt.rt + // step := state.GetStep() + + // Setup expectations + expected := testutil.NewExpectedState(state) + expected.ExpectStep() + expected.Registers[rtReg] = tt.expectedResult + // stepWitness, err := goVm.Step(true) + _, err := goVm.Step(true) + require.NoError(t, err) + + // Check expectations + expected.Validate(t, state) + // TODO(pcw109550): Implement onchaim VM for mips64r2 + // testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts) + }) + } + } +}