Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cannon/mipsevm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Supported 63 instructions:
| `Conditional Branch` | `bne` | Branch on not equal. |
| `Logical` | `clo` | Count leading ones. |
| `Logical` | `clz` | Count leading zeros. |
| `Logical` | `dclo` | Count Leading Ones in Doubleword. |
| `Logical` | `dclz` | Count Leading Zeros in Doubleword. |
| `Arithmetic` | `div` | Divide. |
| `Arithmetic` | `divu` | Divide unsigned. |
| `Unconditional Jump` | `j` | Jump. |
Expand Down
22 changes: 16 additions & 6 deletions cannon/mipsevm/exec/mips_instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func GetInstructionDetails(pc Word, memory *memory.Memory) (insn, opcode, fun ui

// ExecMipsCoreStepLogic executes a MIPS instruction that isn't a syscall nor a RMW operation
// If a store operation occurred, then it returns the effective address of the store memory location.
func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) (memUpdated bool, effMemAddr Word, err error) {
func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory *memory.Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker, features mipsevm.FeatureToggles) (memUpdated bool, effMemAddr Word, err error) {
// j-type j/jal
if opcode == 2 || opcode == 3 {
linkReg := Word(0)
Expand Down Expand Up @@ -117,7 +117,7 @@ func ExecMipsCoreStepLogic(cpu *mipsevm.CpuScalars, registers *[32]Word, memory
}

// ALU
val := ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem)
val := ExecuteMipsInstruction(insn, opcode, fun, rs, rt, mem, features)

funSel := uint32(0x1c)
if !arch.IsMips32 {
Expand Down Expand Up @@ -182,7 +182,7 @@ func assertMips64Fun(fun uint32) {
}
}

func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem Word) Word {
func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem Word, features mipsevm.FeatureToggles) Word {
if opcode == 0 || (opcode >= 8 && opcode < 0xF) || (!arch.IsMips32 && (opcode == 0x18 || opcode == 0x19)) {
// transform ArithLogI to SPECIAL
switch opcode {
Expand Down Expand Up @@ -338,10 +338,10 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem
switch opcode {
// SPECIAL2
case 0x1C:
switch fun {
case 0x2: // mul
switch {
case fun == 0x2: // mul
return SignExtend(Word(int32(rs)*int32(rt)), 32)
case 0x20, 0x21: // clz, clo
case fun == 0x20 || fun == 0x21: // clz, clo
if fun == 0x20 {
rs = ^rs
}
Expand All @@ -350,6 +350,16 @@ func ExecuteMipsInstruction(insn uint32, opcode uint32, fun uint32, rs, rt, mem
rs <<= 1
}
return Word(i)
case features.SupportDclzDclo && (fun == 0x24 || fun == 0x25): // dclz, dclo
assertMips64Fun(insn)
if fun == 0x24 {
rs = ^rs
}
i := uint32(0)
for ; uint64(rs)&0x80000000_00000000 != 0; i++ {
rs <<= 1
}
return Word(i)
}
case 0x0F: // lui
return SignExtend(rt<<16, 32)
Expand Down
1 change: 1 addition & 0 deletions cannon/mipsevm/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type Metadata interface {
// version can then be supported via multicannon pulling in a specific build and support for it dropped in latest code.
type FeatureToggles struct {
SupportNoopSysEventFd2 bool
SupportDclzDclo bool
}

type FPVM interface {
Expand Down
2 changes: 1 addition & 1 deletion cannon/mipsevm/multithreaded/mips.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func (m *InstrumentedState) doMipsStep() error {
}

// Exec the rest of the step logic
memUpdated, effMemAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker)
memUpdated, effMemAddr, err := exec.ExecMipsCoreStepLogic(m.state.getCpuRef(), m.state.GetRegistersRef(), m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker, m.features)
if err != nil {
return err
}
Expand Down
77 changes: 77 additions & 0 deletions cannon/mipsevm/tests/evm_common64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package tests
import (
"fmt"
"os"
"slices"
"testing"

"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -508,3 +511,77 @@ func TestEVM_SingleStep_Branch64(t *testing.T) {

testBranch(t, cases)
}

func TestEVM_SingleStep_DCloDClz64(t *testing.T) {
rsReg := uint32(7)
rdReg := uint32(8)
cases := []struct {
name string
rs Word
funct uint32
expectedResult Word
}{
// dclo
{name: "dclo", rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FF), expectedResult: Word(64), funct: 0b10_0101},
{name: "dclo", rs: Word(0xFF_FF_FF_FF_FF_FF_FF_FE), expectedResult: Word(63), funct: 0b10_0101},
{name: "dclo", rs: Word(0xFF_FF_FF_FF_00_00_00_00), expectedResult: Word(32), funct: 0b10_0101},
{name: "dclo", rs: Word(0x80_00_00_00_00_00_00_00), expectedResult: Word(1), funct: 0b10_0101},
{name: "dclo", rs: Word(0x0), expectedResult: Word(0), funct: 0b10_0101},
// 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},
}

vmVersions := GetMipsVersionTestCases(t)
require.True(t, slices.ContainsFunc(vmVersions, func(v VersionedVMTestCase) bool {
features := versions.FeaturesForVersion(v.Version)
return features.SupportDclzDclo
}), "dclz/dclo feature not tested")
require.True(t, slices.ContainsFunc(vmVersions, func(v VersionedVMTestCase) bool {
features := versions.FeaturesForVersion(v.Version)
return !features.SupportDclzDclo
}), "dclz/dclo backwards compatibility feature not tested")

for _, v := range vmVersions {
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()

features := versions.FeaturesForVersion(v.Version)
if features.SupportDclzDclo {
expected := testutil.NewExpectedState(state)
expected.ExpectStep()
expected.Registers[rdReg] = tt.expectedResult
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
expected.Validate(t, state)
testutil.ValidateEVM(t, stepWitness, step, goVm, v.StateHashFn, v.Contracts)
} else {
assertUnsupportedInstruction(t, v, insn, goVm)
}
})
}
}
}

func assertUnsupportedInstruction(t *testing.T, versionedTestCase VersionedVMTestCase, insn uint32, goVm mipsevm.FPVM) {
state := goVm.GetState()
proofData := versionedTestCase.ProofGenerator(t, goVm.GetState())
goPanicMsg := fmt.Sprintf("invalid instruction: %x", insn)
require.PanicsWithValue(t, goPanicMsg, func() {
_, _ = goVm.Step(
false)
})
errMsg := testutil.CreateErrorStringMatcher("invalid instruction")
testutil.AssertEVMReverts(t, state, versionedTestCase.Contracts, nil, proofData, errMsg)
}
1 change: 1 addition & 0 deletions cannon/mipsevm/versions/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func FeaturesForVersion(version StateVersion) mipsevm.FeatureToggles {
// Set any required feature toggles based on the state version here.
if version >= VersionMultiThreaded64_v4 {
features.SupportNoopSysEventFd2 = true
features.SupportDclzDclo = true
}
return features
}
Expand Down
2 changes: 1 addition & 1 deletion cannon/mipsevm/versions/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
VersionMultiThreaded_v2
// VersionMultiThreaded64_v3 includes futex handling simplification
VersionMultiThreaded64_v3
// VersionMultiThreaded64_v4 is the latest 64-bit multithreaded vm, includes support for new syscall eventfd2
// VersionMultiThreaded64_v4 is the latest 64-bit multithreaded vm, includes support for new syscall eventfd2 and dclo/dclz instructions
VersionMultiThreaded64_v4
)

Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@
"sourceCodeHash": "0x734a6b2aa6406bc145d848ad6071d3af1d40852aeb8f4b2f6f51beaad476e2d3"
},
"src/cannon/MIPS64.sol:MIPS64": {
"initCodeHash": "0xd38d92ed0ddeba1ecb7ae65dd4142a91b51c24c15f0e449f3c711e86b97db1ab",
"sourceCodeHash": "0x9c01b1c4611583819dbc4dfe529fc1d6ca5885620e89cc58668e0606d5bf959a"
"initCodeHash": "0xcbe1c834c7f1c954ccad3e613f440fed6732dccc0a8786dac8d831752d5613f3",
"sourceCodeHash": "0x59352159a7c46f8cf9a408b20f90e6802ad78ee65bbc215cb825ecfef7beebc1"
},
"src/cannon/PreimageOracle.sol:PreimageOracle": {
"initCodeHash": "0x6af5b0e83b455aab8d0946c160a4dc049a4e03be69f8a2a9e87b574f27b25a66",
Expand Down
9 changes: 5 additions & 4 deletions packages/contracts-bedrock/src/cannon/MIPS64.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ contract MIPS64 is ISemver {
}

/// @notice The semantic version of the MIPS64 contract.
/// @custom:semver 1.2.0
string public constant version = "1.2.0";
/// @custom:semver 1.2.1
string public constant version = "1.2.1";

/// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE;
Expand Down Expand Up @@ -273,7 +273,8 @@ contract MIPS64 is ISemver {
memProofOffset: MIPS64Memory.memoryProofOffset(MEM_PROOF_OFFSET, 1),
insn: insn,
opcode: opcode,
fun: fun
fun: fun,
stateVersion: STATE_VERSION
});
bool memUpdated;
uint64 effMemAddr;
Expand Down Expand Up @@ -622,7 +623,7 @@ contract MIPS64 is ISemver {
} else if (syscall_no == sys.SYS_LSEEK) {
// ignored
} else if (syscall_no == sys.SYS_EVENTFD2) {
if (STATE_VERSION < 7) {
if (!st.featuresForVersion(STATE_VERSION).supportNoopSysEventFd2) {
revert("MIPS64: unimplemented syscall");
}
} else {
Expand Down
Loading