Commit cce7f9c3 authored by mbaxter's avatar mbaxter Committed by GitHub

cannon: Extract MIPS step helper functions (#11017)

* cannon: Extract step helpers

* cannon: Wrap step helper logic in unchecked

* cannon: Use consistent var name between solidity and go (fun)

* cannon: Dedupe `opcode` and `fun` calculations

* cannon: Bump MIPS.sol version

* cannon: Run semver-lock, snapshots

* cannon: Address slither warnings

* cannon: Make sure all lib functions are unchecked
parent 1f64dd6d
......@@ -8,6 +8,8 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
)
type MemTracker func(addr uint32)
func (m *InstrumentedState) readPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) {
preimage := m.lastPreimage
if key != m.lastPreimageKey {
......@@ -123,117 +125,14 @@ func (m *InstrumentedState) mipsStep() error {
}
m.state.Step += 1
// instruction fetch
insn := m.state.Memory.GetMemory(m.state.Cpu.PC)
opcode := insn >> 26 // 6-bits
// j-type j/jal
if opcode == 2 || opcode == 3 {
linkReg := uint32(0)
if opcode == 3 {
linkReg = 31
}
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset
target := (m.state.Cpu.NextPC & 0xF0000000) | ((insn & 0x03FFFFFF) << 2)
m.pushStack(target)
return handleJump(&m.state.Cpu, &m.state.Registers, linkReg, target)
}
// register fetch
rs := uint32(0) // source register 1 value
rt := uint32(0) // source register 2 / temp value
rtReg := (insn >> 16) & 0x1F
// R-type or I-type (stores rt)
rs = m.state.Registers[(insn>>21)&0x1F]
rdReg := rtReg
if opcode == 0 || opcode == 0x1c {
// R-type (stores rd)
rt = m.state.Registers[rtReg]
rdReg = (insn >> 11) & 0x1F
} else if opcode < 0x20 {
// rt is SignExtImm
// don't sign extend for andi, ori, xori
if opcode == 0xC || opcode == 0xD || opcode == 0xe {
// ZeroExtImm
rt = insn & 0xFFFF
} else {
// SignExtImm
rt = signExtend(insn&0xFFFF, 16)
}
} else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 {
// store rt value with store
rt = m.state.Registers[rtReg]
// store actual rt with lwl and lwr
rdReg = rtReg
}
if (opcode >= 4 && opcode < 8) || opcode == 1 {
return handleBranch(&m.state.Cpu, &m.state.Registers, opcode, insn, rtReg, rs)
}
storeAddr := uint32(0xFF_FF_FF_FF)
// memory fetch (all I-type)
// we do the load for stores also
mem := uint32(0)
if opcode >= 0x20 {
// M[R[rs]+SignExtImm]
rs += signExtend(insn&0xFFFF, 16)
addr := rs & 0xFFFFFFFC
m.trackMemAccess(addr)
mem = m.state.Memory.GetMemory(addr)
if opcode >= 0x28 && opcode != 0x30 {
// store
storeAddr = addr
// store opcodes don't write back to a register
rdReg = 0
}
}
// ALU
val := executeMipsInstruction(insn, rs, rt, mem)
fun := insn & 0x3f // 6-bits
if opcode == 0 && fun >= 8 && fun < 0x1c {
if fun == 8 || fun == 9 { // jr/jalr
linkReg := uint32(0)
if fun == 9 {
linkReg = rdReg
}
m.popStack()
return handleJump(&m.state.Cpu, &m.state.Registers, linkReg, rs)
}
if fun == 0xa { // movz
return handleRd(&m.state.Cpu, &m.state.Registers, rdReg, rs, rt == 0)
}
if fun == 0xb { // movn
return handleRd(&m.state.Cpu, &m.state.Registers, rdReg, rs, rt != 0)
}
insn, opcode, fun := getInstructionDetails(m.state.Cpu.PC, m.state.Memory)
// Handle syscall separately
// syscall (can read and write)
if fun == 0xC {
if opcode == 0 && fun == 0xC {
return m.handleSyscall()
}
// lo and hi registers
// can write back
if fun >= 0x10 && fun < 0x1c {
return handleHiLo(&m.state.Cpu, &m.state.Registers, fun, rs, rt, rdReg)
}
}
// stupid sc, write a 1 to rt
if opcode == 0x38 && rtReg != 0 {
m.state.Registers[rtReg] = 1
}
// write memory
if storeAddr != 0xFF_FF_FF_FF {
m.trackMemAccess(storeAddr)
m.state.Memory.SetMemory(storeAddr, val)
}
// write back the value to destination register
return handleRd(&m.state.Cpu, &m.state.Registers, rdReg, val, true)
// Exec the rest of the step logic
return execMipsCoreStepLogic(&m.state.Cpu, &m.state.Registers, m.state.Memory, insn, opcode, fun, m.trackMemAccess, m)
}
package mipsevm
func executeMipsInstruction(insn uint32, rs uint32, rt uint32, mem uint32) uint32 {
opcode := insn >> 26 // 6-bits
type StackTracker interface {
pushStack(target uint32)
popStack()
}
func getInstructionDetails(pc uint32, memory *Memory) (insn, opcode, fun uint32) {
insn = memory.GetMemory(pc)
opcode = insn >> 26 // First 6-bits
fun = insn & 0x3f // Last 6-bits
return insn, opcode, fun
}
func execMipsCoreStepLogic(cpu *CpuScalars, registers *[32]uint32, memory *Memory, insn, opcode, fun uint32, memTracker MemTracker, stackTracker StackTracker) error {
// j-type j/jal
if opcode == 2 || opcode == 3 {
linkReg := uint32(0)
if opcode == 3 {
linkReg = 31
}
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset
target := (cpu.NextPC & 0xF0000000) | ((insn & 0x03FFFFFF) << 2)
stackTracker.pushStack(target)
return handleJump(cpu, registers, linkReg, target)
}
// register fetch
rs := uint32(0) // source register 1 value
rt := uint32(0) // source register 2 / temp value
rtReg := (insn >> 16) & 0x1F
// R-type or I-type (stores rt)
rs = registers[(insn>>21)&0x1F]
rdReg := rtReg
if opcode == 0 || opcode == 0x1c {
// R-type (stores rd)
rt = registers[rtReg]
rdReg = (insn >> 11) & 0x1F
} else if opcode < 0x20 {
// rt is SignExtImm
// don't sign extend for andi, ori, xori
if opcode == 0xC || opcode == 0xD || opcode == 0xe {
// ZeroExtImm
rt = insn & 0xFFFF
} else {
// SignExtImm
rt = signExtend(insn&0xFFFF, 16)
}
} else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 {
// store rt value with store
rt = registers[rtReg]
// store actual rt with lwl and lwr
rdReg = rtReg
}
if (opcode >= 4 && opcode < 8) || opcode == 1 {
return handleBranch(cpu, registers, opcode, insn, rtReg, rs)
}
storeAddr := uint32(0xFF_FF_FF_FF)
// memory fetch (all I-type)
// we do the load for stores also
mem := uint32(0)
if opcode >= 0x20 {
// M[R[rs]+SignExtImm]
rs += signExtend(insn&0xFFFF, 16)
addr := rs & 0xFFFFFFFC
memTracker(addr)
mem = memory.GetMemory(addr)
if opcode >= 0x28 && opcode != 0x30 {
// store
storeAddr = addr
// store opcodes don't write back to a register
rdReg = 0
}
}
// ALU
val := executeMipsInstruction(insn, opcode, fun, rs, rt, mem)
if opcode == 0 && fun >= 8 && fun < 0x1c {
if fun == 8 || fun == 9 { // jr/jalr
linkReg := uint32(0)
if fun == 9 {
linkReg = rdReg
}
stackTracker.popStack()
return handleJump(cpu, registers, linkReg, rs)
}
if fun == 0xa { // movz
return handleRd(cpu, registers, rdReg, rs, rt == 0)
}
if fun == 0xb { // movn
return handleRd(cpu, registers, rdReg, rs, rt != 0)
}
// lo and hi registers
// can write back
if fun >= 0x10 && fun < 0x1c {
return handleHiLo(cpu, registers, fun, rs, rt, rdReg)
}
}
// store conditional, write a 1 to rt
if opcode == 0x38 && rtReg != 0 {
registers[rtReg] = 1
}
// write memory
if storeAddr != 0xFF_FF_FF_FF {
memTracker(storeAddr)
memory.SetMemory(storeAddr, val)
}
// write back the value to destination register
return handleRd(cpu, registers, rdReg, val, true)
}
func executeMipsInstruction(insn, opcode, fun, rs, rt, mem uint32) uint32 {
if opcode == 0 || (opcode >= 8 && opcode < 0xF) {
fun := insn & 0x3f // 6-bits
// transform ArithLogI to SPECIAL
switch opcode {
case 8:
......@@ -101,7 +218,6 @@ func executeMipsInstruction(insn uint32, rs uint32, rt uint32, mem uint32) uint3
switch opcode {
// SPECIAL2
case 0x1C:
fun := insn & 0x3f // 6-bits
switch fun {
case 0x2: // mul
return uint32(int32(rs) * int32(rt))
......
......@@ -34,7 +34,6 @@ const (
)
type PreimageReader func(key [32]byte, offset uint32) (dat [32]byte, datLen uint32)
type MemTracker func(addr uint32)
func getSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2 uint32) {
syscallNum = registers[2] // v0
......
......@@ -124,8 +124,8 @@
"sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f"
},
"src/cannon/MIPS.sol": {
"initCodeHash": "0xe9183ee3b69d9ec9594d6b3923d78c86c996cd738ccbc09675bb281284c060af",
"sourceCodeHash": "0x7c2eab73da8b2eeadba30eadb39f20e91307bc29218938fadfc5f73fadcf13bc"
"initCodeHash": "0xe2cfbfa5d8587a6c3cf52686e29fb0d07e2764af0ef728825529f42ebdeacb5d",
"sourceCodeHash": "0x231f42a05f0c8e5784eb112518afca0bb16a3689f317ce021b8390a0aa70377b"
},
"src/cannon/PreimageOracle.sol": {
"initCodeHash": "0xe5db668fe41436f53995e910488c7c140766ba8745e19743773ebab508efd090",
......
......@@ -48,7 +48,7 @@ contract MIPS is ISemver {
/// @notice The semantic version of the MIPS contract.
/// @custom:semver 1.0.1
string public constant version = "1.1.0-beta.4";
string public constant version = "1.1.0-beta.5";
/// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE;
......@@ -262,178 +262,30 @@ contract MIPS is ISemver {
// instruction fetch
uint256 insnProofOffset = MIPSMemory.memoryProofOffset(STEP_PROOF_OFFSET, 0);
uint32 insn = MIPSMemory.readMem(state.memRoot, state.pc, insnProofOffset);
uint32 opcode = insn >> 26; // 6-bits
// j-type j/jal
if (opcode == 2 || opcode == 3) {
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset
uint32 target = (state.nextPC & 0xF0000000) | (insn & 0x03FFFFFF) << 2;
return handleJumpAndReturnOutput(state, opcode == 2 ? 0 : 31, target);
}
// register fetch
uint32 rs; // source register 1 value
uint32 rt; // source register 2 / temp value
uint32 rtReg = (insn >> 16) & 0x1F;
// R-type or I-type (stores rt)
rs = state.registers[(insn >> 21) & 0x1F];
uint32 rdReg = rtReg;
if (opcode == 0 || opcode == 0x1c) {
// R-type (stores rd)
rt = state.registers[rtReg];
rdReg = (insn >> 11) & 0x1F;
} else if (opcode < 0x20) {
// rt is SignExtImm
// don't sign extend for andi, ori, xori
if (opcode == 0xC || opcode == 0xD || opcode == 0xe) {
// ZeroExtImm
rt = insn & 0xFFFF;
} else {
// SignExtImm
rt = ins.signExtend(insn & 0xFFFF, 16);
}
} else if (opcode >= 0x28 || opcode == 0x22 || opcode == 0x26) {
// store rt value with store
rt = state.registers[rtReg];
// store actual rt with lwl and lwr
rdReg = rtReg;
}
if ((opcode >= 4 && opcode < 8) || opcode == 1) {
st.CpuScalars memory cpu = getCpuScalars(state);
ins.handleBranch({
_cpu: cpu,
_registers: state.registers,
_opcode: opcode,
_insn: insn,
_rtReg: rtReg,
_rs: rs
});
setStateCpuScalars(state, cpu);
return outputState();
}
uint32 storeAddr = 0xFF_FF_FF_FF;
// memory fetch (all I-type)
// we do the load for stores also
uint32 mem;
if (opcode >= 0x20) {
// M[R[rs]+SignExtImm]
rs += ins.signExtend(insn & 0xFFFF, 16);
uint32 addr = rs & 0xFFFFFFFC;
uint256 memProofOffset = MIPSMemory.memoryProofOffset(STEP_PROOF_OFFSET, 1);
mem = MIPSMemory.readMem(state.memRoot, addr, memProofOffset);
if (opcode >= 0x28 && opcode != 0x30) {
// store
storeAddr = addr;
// store opcodes don't write back to a register
rdReg = 0;
}
}
// ALU
// Note: swr outputs more than 4 bytes without the mask 0xffFFffFF
uint32 val = ins.executeMipsInstruction(insn, rs, rt, mem) & 0xffFFffFF;
uint32 func = insn & 0x3f; // 6-bits
if (opcode == 0 && func >= 8 && func < 0x1c) {
if (func == 8 || func == 9) {
// jr/jalr
return handleJumpAndReturnOutput(state, func == 8 ? 0 : rdReg, rs);
}
if (func == 0xa) {
// movz
return handleRdAndReturnOutput(state, rdReg, rs, rt == 0);
}
if (func == 0xb) {
// movn
return handleRdAndReturnOutput(state, rdReg, rs, rt != 0);
}
(uint32 insn, uint32 opcode, uint32 fun) =
ins.getInstructionDetails(state.pc, state.memRoot, insnProofOffset);
// Handle syscall separately
// syscall (can read and write)
if (func == 0xC) {
if (opcode == 0 && fun == 0xC) {
return handleSyscall(_localContext);
}
// lo and hi registers
// can write back
if (func >= 0x10 && func < 0x1c) {
// Exec the rest of the step logic
st.CpuScalars memory cpu = getCpuScalars(state);
ins.handleHiLo({
(state.memRoot) = ins.execMipsCoreStepLogic({
_cpu: cpu,
_registers: state.registers,
_func: func,
_rs: rs,
_rt: rt,
_storeReg: rdReg
_memRoot: state.memRoot,
_memProofOffset: MIPSMemory.memoryProofOffset(STEP_PROOF_OFFSET, 1),
_insn: insn,
_opcode: opcode,
_fun: fun
});
setStateCpuScalars(state, cpu);
return outputState();
}
}
// stupid sc, write a 1 to rt
if (opcode == 0x38 && rtReg != 0) {
state.registers[rtReg] = 1;
}
// write memory
if (storeAddr != 0xFF_FF_FF_FF) {
uint256 memProofOffset = MIPSMemory.memoryProofOffset(STEP_PROOF_OFFSET, 1);
state.memRoot = MIPSMemory.writeMem(storeAddr, memProofOffset, val);
}
// write back the value to destination register
return handleRdAndReturnOutput(state, rdReg, val, true);
}
}
function handleJumpAndReturnOutput(
State memory _state,
uint32 _linkReg,
uint32 _dest
)
internal
returns (bytes32 out_)
{
st.CpuScalars memory cpu = getCpuScalars(_state);
ins.handleJump({ _cpu: cpu, _registers: _state.registers, _linkReg: _linkReg, _dest: _dest });
setStateCpuScalars(_state, cpu);
return outputState();
}
function handleRdAndReturnOutput(
State memory _state,
uint32 _storeReg,
uint32 _val,
bool _conditional
)
internal
returns (bytes32 out_)
{
st.CpuScalars memory cpu = getCpuScalars(_state);
ins.handleRd({
_cpu: cpu,
_registers: _state.registers,
_storeReg: _storeReg,
_val: _val,
_conditional: _conditional
});
setStateCpuScalars(_state, cpu);
return outputState();
}
function getCpuScalars(State memory _state) internal pure returns (st.CpuScalars memory) {
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { MIPSMemory } from "src/cannon/libraries/MIPSMemory.sol";
import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol";
library MIPSInstructions {
/// @param _pc The program counter.
/// @param _memRoot The current memory root.
/// @param _insnProofOffset The calldata offset of the memory proof for the current instruction.
/// @return insn_ The current 32-bit instruction at the pc.
/// @return opcode_ The opcode value parsed from insn_.
/// @return fun_ The function value parsed from insn_.
function getInstructionDetails(
uint32 _pc,
bytes32 _memRoot,
uint256 _insnProofOffset
)
internal
pure
returns (uint32 insn_, uint32 opcode_, uint32 fun_)
{
unchecked {
insn_ = MIPSMemory.readMem(_memRoot, _pc, _insnProofOffset);
opcode_ = insn_ >> 26; // First 6-bits
fun_ = insn_ & 0x3f; // Last 6-bits
return (insn_, opcode_, fun_);
}
}
/// @notice Execute core MIPS step logic.
/// @notice _cpu The CPU scalar fields.
/// @notice _registers The CPU registers.
/// @notice _memRoot The current merkle root of the memory.
/// @notice _memProofOffset The offset in calldata specify where the memory merkle proof is located.
/// @param _insn The current 32-bit instruction at the pc.
/// @param _opcode The opcode value parsed from insn_.
/// @param _fun The function value parsed from insn_.
/// @return newMemRoot_ The updated merkle root of memory after any modifications, may be unchanged.
function execMipsCoreStepLogic(
st.CpuScalars memory _cpu,
uint32[32] memory _registers,
bytes32 _memRoot,
uint256 _memProofOffset,
uint32 _insn,
uint32 _opcode,
uint32 _fun
)
internal
pure
returns (bytes32 newMemRoot_)
{
unchecked {
newMemRoot_ = _memRoot;
// j-type j/jal
if (_opcode == 2 || _opcode == 3) {
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset
uint32 target = (_cpu.nextPC & 0xF0000000) | (_insn & 0x03FFFFFF) << 2;
handleJump(_cpu, _registers, _opcode == 2 ? 0 : 31, target);
return newMemRoot_;
}
// register fetch
uint32 rs = 0; // source register 1 value
uint32 rt = 0; // source register 2 / temp value
uint32 rtReg = (_insn >> 16) & 0x1F;
// R-type or I-type (stores rt)
rs = _registers[(_insn >> 21) & 0x1F];
uint32 rdReg = rtReg;
if (_opcode == 0 || _opcode == 0x1c) {
// R-type (stores rd)
rt = _registers[rtReg];
rdReg = (_insn >> 11) & 0x1F;
} else if (_opcode < 0x20) {
// rt is SignExtImm
// don't sign extend for andi, ori, xori
if (_opcode == 0xC || _opcode == 0xD || _opcode == 0xe) {
// ZeroExtImm
rt = _insn & 0xFFFF;
} else {
// SignExtImm
rt = signExtend(_insn & 0xFFFF, 16);
}
} else if (_opcode >= 0x28 || _opcode == 0x22 || _opcode == 0x26) {
// store rt value with store
rt = _registers[rtReg];
// store actual rt with lwl and lwr
rdReg = rtReg;
}
if ((_opcode >= 4 && _opcode < 8) || _opcode == 1) {
handleBranch({
_cpu: _cpu,
_registers: _registers,
_opcode: _opcode,
_insn: _insn,
_rtReg: rtReg,
_rs: rs
});
return newMemRoot_;
}
uint32 storeAddr = 0xFF_FF_FF_FF;
// memory fetch (all I-type)
// we do the load for stores also
uint32 mem = 0;
if (_opcode >= 0x20) {
// M[R[rs]+SignExtImm]
rs += signExtend(_insn & 0xFFFF, 16);
uint32 addr = rs & 0xFFFFFFFC;
mem = MIPSMemory.readMem(_memRoot, addr, _memProofOffset);
if (_opcode >= 0x28 && _opcode != 0x30) {
// store
storeAddr = addr;
// store opcodes don't write back to a register
rdReg = 0;
}
}
// ALU
// Note: swr outputs more than 4 bytes without the mask 0xffFFffFF
uint32 val = executeMipsInstruction(_insn, _opcode, _fun, rs, rt, mem) & 0xffFFffFF;
if (_opcode == 0 && _fun >= 8 && _fun < 0x1c) {
if (_fun == 8 || _fun == 9) {
// jr/jalr
handleJump(_cpu, _registers, _fun == 8 ? 0 : rdReg, rs);
return newMemRoot_;
}
if (_fun == 0xa) {
// movz
handleRd(_cpu, _registers, rdReg, rs, rt == 0);
return newMemRoot_;
}
if (_fun == 0xb) {
// movn
handleRd(_cpu, _registers, rdReg, rs, rt != 0);
return newMemRoot_;
}
// lo and hi registers
// can write back
if (_fun >= 0x10 && _fun < 0x1c) {
handleHiLo({ _cpu: _cpu, _registers: _registers, _fun: _fun, _rs: rs, _rt: rt, _storeReg: rdReg });
return newMemRoot_;
}
}
// stupid sc, write a 1 to rt
if (_opcode == 0x38 && rtReg != 0) {
_registers[rtReg] = 1;
}
// write memory
if (storeAddr != 0xFF_FF_FF_FF) {
newMemRoot_ = MIPSMemory.writeMem(storeAddr, _memProofOffset, val);
}
// write back the value to destination register
handleRd(_cpu, _registers, rdReg, val, true);
return newMemRoot_;
}
}
/// @notice Execute an instruction.
function executeMipsInstruction(
uint32 _insn,
uint32 _opcode,
uint32 _fun,
uint32 _rs,
uint32 _rt,
uint32 _mem
......@@ -16,167 +184,163 @@ library MIPSInstructions {
returns (uint32 out_)
{
unchecked {
uint32 opcode = _insn >> 26; // 6-bits
if (opcode == 0 || (opcode >= 8 && opcode < 0xF)) {
uint32 func = _insn & 0x3f; // 6-bits
if (_opcode == 0 || (_opcode >= 8 && _opcode < 0xF)) {
assembly {
// transform ArithLogI to SPECIAL
switch opcode
switch _opcode
// addi
case 0x8 { func := 0x20 }
case 0x8 { _fun := 0x20 }
// addiu
case 0x9 { func := 0x21 }
case 0x9 { _fun := 0x21 }
// stli
case 0xA { func := 0x2A }
case 0xA { _fun := 0x2A }
// sltiu
case 0xB { func := 0x2B }
case 0xB { _fun := 0x2B }
// andi
case 0xC { func := 0x24 }
case 0xC { _fun := 0x24 }
// ori
case 0xD { func := 0x25 }
case 0xD { _fun := 0x25 }
// xori
case 0xE { func := 0x26 }
case 0xE { _fun := 0x26 }
}
// sll
if (func == 0x00) {
if (_fun == 0x00) {
return _rt << ((_insn >> 6) & 0x1F);
}
// srl
else if (func == 0x02) {
else if (_fun == 0x02) {
return _rt >> ((_insn >> 6) & 0x1F);
}
// sra
else if (func == 0x03) {
else if (_fun == 0x03) {
uint32 shamt = (_insn >> 6) & 0x1F;
return signExtend(_rt >> shamt, 32 - shamt);
}
// sllv
else if (func == 0x04) {
else if (_fun == 0x04) {
return _rt << (_rs & 0x1F);
}
// srlv
else if (func == 0x6) {
else if (_fun == 0x6) {
return _rt >> (_rs & 0x1F);
}
// srav
else if (func == 0x07) {
else if (_fun == 0x07) {
return signExtend(_rt >> _rs, 32 - _rs);
}
// functs in range [0x8, 0x1b] are handled specially by other functions
// Explicitly enumerate each funct in range to reduce code diff against Go Vm
// jr
else if (func == 0x08) {
else if (_fun == 0x08) {
return _rs;
}
// jalr
else if (func == 0x09) {
else if (_fun == 0x09) {
return _rs;
}
// movz
else if (func == 0x0a) {
else if (_fun == 0x0a) {
return _rs;
}
// movn
else if (func == 0x0b) {
else if (_fun == 0x0b) {
return _rs;
}
// syscall
else if (func == 0x0c) {
else if (_fun == 0x0c) {
return _rs;
}
// 0x0d - break not supported
// sync
else if (func == 0x0f) {
else if (_fun == 0x0f) {
return _rs;
}
// mfhi
else if (func == 0x10) {
else if (_fun == 0x10) {
return _rs;
}
// mthi
else if (func == 0x11) {
else if (_fun == 0x11) {
return _rs;
}
// mflo
else if (func == 0x12) {
else if (_fun == 0x12) {
return _rs;
}
// mtlo
else if (func == 0x13) {
else if (_fun == 0x13) {
return _rs;
}
// mult
else if (func == 0x18) {
else if (_fun == 0x18) {
return _rs;
}
// multu
else if (func == 0x19) {
else if (_fun == 0x19) {
return _rs;
}
// div
else if (func == 0x1a) {
else if (_fun == 0x1a) {
return _rs;
}
// divu
else if (func == 0x1b) {
else if (_fun == 0x1b) {
return _rs;
}
// The rest includes transformed R-type arith imm instructions
// add
else if (func == 0x20) {
else if (_fun == 0x20) {
return (_rs + _rt);
}
// addu
else if (func == 0x21) {
else if (_fun == 0x21) {
return (_rs + _rt);
}
// sub
else if (func == 0x22) {
else if (_fun == 0x22) {
return (_rs - _rt);
}
// subu
else if (func == 0x23) {
else if (_fun == 0x23) {
return (_rs - _rt);
}
// and
else if (func == 0x24) {
else if (_fun == 0x24) {
return (_rs & _rt);
}
// or
else if (func == 0x25) {
else if (_fun == 0x25) {
return (_rs | _rt);
}
// xor
else if (func == 0x26) {
else if (_fun == 0x26) {
return (_rs ^ _rt);
}
// nor
else if (func == 0x27) {
else if (_fun == 0x27) {
return ~(_rs | _rt);
}
// slti
else if (func == 0x2a) {
else if (_fun == 0x2a) {
return int32(_rs) < int32(_rt) ? 1 : 0;
}
// sltiu
else if (func == 0x2b) {
else if (_fun == 0x2b) {
return _rs < _rt ? 1 : 0;
} else {
revert("invalid instruction");
}
} else {
// SPECIAL2
if (opcode == 0x1C) {
uint32 func = _insn & 0x3f; // 6-bits
if (_opcode == 0x1C) {
// mul
if (func == 0x2) {
if (_fun == 0x2) {
return uint32(int32(_rs) * int32(_rt));
}
// clz, clo
else if (func == 0x20 || func == 0x21) {
if (func == 0x20) {
else if (_fun == 0x20 || _fun == 0x21) {
if (_fun == 0x20) {
_rs = ~_rs;
}
uint32 i = 0;
......@@ -188,75 +352,75 @@ library MIPSInstructions {
}
}
// lui
else if (opcode == 0x0F) {
else if (_opcode == 0x0F) {
return _rt << 16;
}
// lb
else if (opcode == 0x20) {
else if (_opcode == 0x20) {
return signExtend((_mem >> (24 - (_rs & 3) * 8)) & 0xFF, 8);
}
// lh
else if (opcode == 0x21) {
else if (_opcode == 0x21) {
return signExtend((_mem >> (16 - (_rs & 2) * 8)) & 0xFFFF, 16);
}
// lwl
else if (opcode == 0x22) {
else if (_opcode == 0x22) {
uint32 val = _mem << ((_rs & 3) * 8);
uint32 mask = uint32(0xFFFFFFFF) << ((_rs & 3) * 8);
return (_rt & ~mask) | val;
}
// lw
else if (opcode == 0x23) {
else if (_opcode == 0x23) {
return _mem;
}
// lbu
else if (opcode == 0x24) {
else if (_opcode == 0x24) {
return (_mem >> (24 - (_rs & 3) * 8)) & 0xFF;
}
// lhu
else if (opcode == 0x25) {
else if (_opcode == 0x25) {
return (_mem >> (16 - (_rs & 2) * 8)) & 0xFFFF;
}
// lwr
else if (opcode == 0x26) {
else if (_opcode == 0x26) {
uint32 val = _mem >> (24 - (_rs & 3) * 8);
uint32 mask = uint32(0xFFFFFFFF) >> (24 - (_rs & 3) * 8);
return (_rt & ~mask) | val;
}
// sb
else if (opcode == 0x28) {
else if (_opcode == 0x28) {
uint32 val = (_rt & 0xFF) << (24 - (_rs & 3) * 8);
uint32 mask = 0xFFFFFFFF ^ uint32(0xFF << (24 - (_rs & 3) * 8));
return (_mem & mask) | val;
}
// sh
else if (opcode == 0x29) {
else if (_opcode == 0x29) {
uint32 val = (_rt & 0xFFFF) << (16 - (_rs & 2) * 8);
uint32 mask = 0xFFFFFFFF ^ uint32(0xFFFF << (16 - (_rs & 2) * 8));
return (_mem & mask) | val;
}
// swl
else if (opcode == 0x2a) {
else if (_opcode == 0x2a) {
uint32 val = _rt >> ((_rs & 3) * 8);
uint32 mask = uint32(0xFFFFFFFF) >> ((_rs & 3) * 8);
return (_mem & ~mask) | val;
}
// sw
else if (opcode == 0x2b) {
else if (_opcode == 0x2b) {
return _rt;
}
// swr
else if (opcode == 0x2e) {
else if (_opcode == 0x2e) {
uint32 val = _rt << (24 - (_rs & 3) * 8);
uint32 mask = uint32(0xFFFFFFFF) << (24 - (_rs & 3) * 8);
return (_mem & ~mask) | val;
}
// ll
else if (opcode == 0x30) {
else if (_opcode == 0x30) {
return _mem;
}
// sc
else if (opcode == 0x38) {
else if (_opcode == 0x38) {
return _rt;
} else {
revert("invalid instruction");
......@@ -345,14 +509,14 @@ library MIPSInstructions {
/// @notice Handles HI and LO register instructions.
/// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo.
/// @param _registers Holds the current state of the cpu registers.
/// @param _func The function code of the instruction.
/// @param _fun The function code of the instruction.
/// @param _rs The value of the RS register.
/// @param _rt The value of the RT register.
/// @param _storeReg The register to store the result in.
function handleHiLo(
st.CpuScalars memory _cpu,
uint32[32] memory _registers,
uint32 _func,
uint32 _fun,
uint32 _rs,
uint32 _rt,
uint32 _storeReg
......@@ -364,29 +528,29 @@ library MIPSInstructions {
uint32 val = 0;
// mfhi: Move the contents of the HI register into the destination
if (_func == 0x10) {
if (_fun == 0x10) {
val = _cpu.hi;
}
// mthi: Move the contents of the source into the HI register
else if (_func == 0x11) {
else if (_fun == 0x11) {
_cpu.hi = _rs;
}
// mflo: Move the contents of the LO register into the destination
else if (_func == 0x12) {
else if (_fun == 0x12) {
val = _cpu.lo;
}
// mtlo: Move the contents of the source into the LO register
else if (_func == 0x13) {
else if (_fun == 0x13) {
_cpu.lo = _rs;
}
// mult: Multiplies `rs` by `rt` and stores the result in HI and LO registers
else if (_func == 0x18) {
else if (_fun == 0x18) {
uint64 acc = uint64(int64(int32(_rs)) * int64(int32(_rt)));
_cpu.hi = uint32(acc >> 32);
_cpu.lo = uint32(acc);
}
// multu: Unsigned multiplies `rs` by `rt` and stores the result in HI and LO registers
else if (_func == 0x19) {
else if (_fun == 0x19) {
uint64 acc = uint64(uint64(_rs) * uint64(_rt));
_cpu.hi = uint32(acc >> 32);
_cpu.lo = uint32(acc);
......@@ -394,7 +558,7 @@ library MIPSInstructions {
// div: Divides `rs` by `rt`.
// Stores the quotient in LO
// And the remainder in HI
else if (_func == 0x1a) {
else if (_fun == 0x1a) {
if (int32(_rt) == 0) {
revert("MIPS: division by zero");
}
......@@ -404,7 +568,7 @@ library MIPSInstructions {
// divu: Unsigned divides `rs` by `rt`.
// Stores the quotient in LO
// And the remainder in HI
else if (_func == 0x1b) {
else if (_fun == 0x1b) {
if (_rt == 0) {
revert("MIPS: division by zero");
}
......
......@@ -93,9 +93,9 @@ library MIPSMemory {
newMemRoot_ := node
}
}
return newMemRoot_;
}
}
/// @notice Computes the offset of a memory proof in the calldata.
/// @param _proofDataOffset The offset of the set of all memory proof data within calldata (proof.offset)
......@@ -114,6 +114,7 @@ library MIPSMemory {
/// @notice Validates that enough calldata is available to hold a full memory proof at the given offset
/// @param _proofStartOffset The index of the first byte of the target memory proof in calldata
function validateMemoryProofAvailability(uint256 _proofStartOffset) internal pure {
unchecked {
uint256 s = 0;
assembly {
s := calldatasize()
......@@ -121,4 +122,5 @@ library MIPSMemory {
// A memory proof consists of 28 bytes32 values - verify we have enough calldata
require(s >= (_proofStartOffset + 28 * 32), "check that there is enough calldata");
}
}
}
......@@ -37,6 +37,7 @@ library MIPSSyscalls {
pure
returns (uint32 sysCallNum_, uint32 a0_, uint32 a1_, uint32 a2_)
{
unchecked {
sysCallNum_ = _registers[2];
a0_ = _registers[4];
......@@ -45,6 +46,7 @@ library MIPSSyscalls {
return (sysCallNum_, a0_, a1_, a2_);
}
}
/// @notice Like a Linux mmap syscall. Allocates a page from the heap.
/// @param _a0 The address for the new mapping
......@@ -62,6 +64,7 @@ library MIPSSyscalls {
pure
returns (uint32 v0_, uint32 v1_, uint32 newHeap_)
{
unchecked {
v1_ = uint32(0);
newHeap_ = _heap;
......@@ -79,6 +82,7 @@ library MIPSSyscalls {
return (v0_, v1_, newHeap_);
}
}
/// @notice Like a Linux read syscall. Splits unaligned reads into aligned reads.
/// @param _a0 The file descriptor.
......@@ -109,6 +113,7 @@ library MIPSSyscalls {
view
returns (uint32 v0_, uint32 v1_, uint32 newPreimageOffset_, bytes32 newMemRoot_)
{
unchecked {
v0_ = uint32(0);
v1_ = uint32(0);
newMemRoot_ = _memRoot;
......@@ -164,6 +169,7 @@ library MIPSSyscalls {
return (v0_, v1_, newPreimageOffset_, newMemRoot_);
}
}
/// @notice Like a Linux write syscall. Splits unaligned writes into aligned writes.
/// @param _a0 The file descriptor.
......@@ -190,6 +196,7 @@ library MIPSSyscalls {
pure
returns (uint32 v0_, uint32 v1_, bytes32 newPreimageKey_, uint32 newPreimageOffset_)
{
unchecked {
// args: _a0 = fd, _a1 = addr, _a2 = count
// returns: v0_ = written, v1_ = err code
v0_ = uint32(0);
......@@ -229,6 +236,7 @@ library MIPSSyscalls {
return (v0_, v1_, newPreimageKey_, newPreimageOffset_);
}
}
/// @notice Like Linux fcntl (file control) syscall, but only supports minimal file-descriptor control commands, to
/// retrieve the file-descriptor R/W flags.
......@@ -237,6 +245,7 @@ library MIPSSyscalls {
/// @param v0_ The file status flag (only supported command is F_GETFL), or -1 on error.
/// @param v1_ An error number, or 0 if there is no error.
function handleSysFcntl(uint32 _a0, uint32 _a1) internal pure returns (uint32 v0_, uint32 v1_) {
unchecked {
v0_ = uint32(0);
v1_ = uint32(0);
......@@ -258,6 +267,7 @@ library MIPSSyscalls {
return (v0_, v1_);
}
}
function handleSyscallUpdates(
st.CpuScalars memory _cpu,
......@@ -268,6 +278,7 @@ library MIPSSyscalls {
internal
pure
{
unchecked {
// Write the results back to the state registers
_registers[2] = _v0;
_registers[7] = _v1;
......@@ -276,4 +287,5 @@ library MIPSSyscalls {
_cpu.pc = _cpu.nextPC;
_cpu.nextPC = _cpu.nextPC + 4;
}
}
}
......@@ -25,7 +25,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865;
address internal constant l1StandardBridgeAddress = 0xb7900B27Be8f0E0fF65d1C3A4671e1220437dd2b;
address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F;
address internal constant mipsAddress = 0x28bF1582225713139c0E898326Db808B6484cFd4;
address internal constant mipsAddress = 0xcdAdd729ca2319E8955240bDb61A6A6A956A7664;
address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535;
address internal constant optimismPortalProxyAddress = 0x978e3286EB805934215a88694d80b09aDed68D90;
address internal constant preimageOracleAddress = 0x3bd7E801E51d48c5d94Ea68e8B801DFFC275De75;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment