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

cannon: Extract handleHiLo, handleJump, handleRd MIPS helpers (#10943)

* cannon: Extract handleHiLo, handleJump, handlRd from mips.go

* cannon: Extract handleHiLo, handleJump, handlRd from MIPS.sol

* cannon: Increment MIPS.sol version

* cannon: Run semver-lock and snapshots

* cannon: Fix slither warning - init variable to zero
parent 0bb839cf
...@@ -222,67 +222,6 @@ func (m *InstrumentedState) Traceback() { ...@@ -222,67 +222,6 @@ func (m *InstrumentedState) Traceback() {
} }
} }
func (m *InstrumentedState) handleHiLo(fun uint32, rs uint32, rt uint32, storeReg uint32) error {
val := uint32(0)
switch fun {
case 0x10: // mfhi
val = m.state.Cpu.HI
case 0x11: // mthi
m.state.Cpu.HI = rs
case 0x12: // mflo
val = m.state.Cpu.LO
case 0x13: // mtlo
m.state.Cpu.LO = rs
case 0x18: // mult
acc := uint64(int64(int32(rs)) * int64(int32(rt)))
m.state.Cpu.HI = uint32(acc >> 32)
m.state.Cpu.LO = uint32(acc)
case 0x19: // multu
acc := uint64(uint64(rs) * uint64(rt))
m.state.Cpu.HI = uint32(acc >> 32)
m.state.Cpu.LO = uint32(acc)
case 0x1a: // div
m.state.Cpu.HI = uint32(int32(rs) % int32(rt))
m.state.Cpu.LO = uint32(int32(rs) / int32(rt))
case 0x1b: // divu
m.state.Cpu.HI = rs % rt
m.state.Cpu.LO = rs / rt
}
if storeReg != 0 {
m.state.Registers[storeReg] = val
}
m.state.Cpu.PC = m.state.Cpu.NextPC
m.state.Cpu.NextPC = m.state.Cpu.NextPC + 4
return nil
}
func (m *InstrumentedState) handleJump(linkReg uint32, dest uint32) error {
if m.state.Cpu.NextPC != m.state.Cpu.PC+4 {
panic("jump in delay slot")
}
prevPC := m.state.Cpu.PC
m.state.Cpu.PC = m.state.Cpu.NextPC
m.state.Cpu.NextPC = dest
if linkReg != 0 {
m.state.Registers[linkReg] = prevPC + 8 // set the link-register to the instr after the delay slot instruction.
}
return nil
}
func (m *InstrumentedState) handleRd(storeReg uint32, val uint32, conditional bool) error {
if storeReg >= 32 {
panic("invalid register")
}
if storeReg != 0 && conditional {
m.state.Registers[storeReg] = val
}
m.state.Cpu.PC = m.state.Cpu.NextPC
m.state.Cpu.NextPC = m.state.Cpu.NextPC + 4
return nil
}
func (m *InstrumentedState) mipsStep() error { func (m *InstrumentedState) mipsStep() error {
if m.state.Exited { if m.state.Exited {
return nil return nil
...@@ -301,7 +240,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -301,7 +240,7 @@ func (m *InstrumentedState) mipsStep() error {
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset // 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) target := (m.state.Cpu.NextPC & 0xF0000000) | ((insn & 0x03FFFFFF) << 2)
m.pushStack(target) m.pushStack(target)
return m.handleJump(linkReg, target) return handleJump(&m.state.Cpu, &m.state.Registers, linkReg, target)
} }
// register fetch // register fetch
...@@ -367,14 +306,14 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -367,14 +306,14 @@ func (m *InstrumentedState) mipsStep() error {
linkReg = rdReg linkReg = rdReg
} }
m.popStack() m.popStack()
return m.handleJump(linkReg, rs) return handleJump(&m.state.Cpu, &m.state.Registers, linkReg, rs)
} }
if fun == 0xa { // movz if fun == 0xa { // movz
return m.handleRd(rdReg, rs, rt == 0) return handleRd(&m.state.Cpu, &m.state.Registers, rdReg, rs, rt == 0)
} }
if fun == 0xb { // movn if fun == 0xb { // movn
return m.handleRd(rdReg, rs, rt != 0) return handleRd(&m.state.Cpu, &m.state.Registers, rdReg, rs, rt != 0)
} }
// syscall (can read and write) // syscall (can read and write)
...@@ -385,7 +324,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -385,7 +324,7 @@ func (m *InstrumentedState) mipsStep() error {
// lo and hi registers // lo and hi registers
// can write back // can write back
if fun >= 0x10 && fun < 0x1c { if fun >= 0x10 && fun < 0x1c {
return m.handleHiLo(fun, rs, rt, rdReg) return handleHiLo(&m.state.Cpu, &m.state.Registers, fun, rs, rt, rdReg)
} }
} }
...@@ -401,5 +340,5 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -401,5 +340,5 @@ func (m *InstrumentedState) mipsStep() error {
} }
// write back the value to destination register // write back the value to destination register
return m.handleRd(rdReg, val, true) return handleRd(&m.state.Cpu, &m.state.Registers, rdReg, val, true)
} }
...@@ -208,3 +208,64 @@ func handleBranch(cpu *CpuScalars, registers *[32]uint32, opcode uint32, insn ui ...@@ -208,3 +208,64 @@ func handleBranch(cpu *CpuScalars, registers *[32]uint32, opcode uint32, insn ui
} }
return nil return nil
} }
func handleHiLo(cpu *CpuScalars, registers *[32]uint32, fun uint32, rs uint32, rt uint32, storeReg uint32) error {
val := uint32(0)
switch fun {
case 0x10: // mfhi
val = cpu.HI
case 0x11: // mthi
cpu.HI = rs
case 0x12: // mflo
val = cpu.LO
case 0x13: // mtlo
cpu.LO = rs
case 0x18: // mult
acc := uint64(int64(int32(rs)) * int64(int32(rt)))
cpu.HI = uint32(acc >> 32)
cpu.LO = uint32(acc)
case 0x19: // multu
acc := uint64(uint64(rs) * uint64(rt))
cpu.HI = uint32(acc >> 32)
cpu.LO = uint32(acc)
case 0x1a: // div
cpu.HI = uint32(int32(rs) % int32(rt))
cpu.LO = uint32(int32(rs) / int32(rt))
case 0x1b: // divu
cpu.HI = rs % rt
cpu.LO = rs / rt
}
if storeReg != 0 {
registers[storeReg] = val
}
cpu.PC = cpu.NextPC
cpu.NextPC = cpu.NextPC + 4
return nil
}
func handleJump(cpu *CpuScalars, registers *[32]uint32, linkReg uint32, dest uint32) error {
if cpu.NextPC != cpu.PC+4 {
panic("jump in delay slot")
}
prevPC := cpu.PC
cpu.PC = cpu.NextPC
cpu.NextPC = dest
if linkReg != 0 {
registers[linkReg] = prevPC + 8 // set the link-register to the instr after the delay slot instruction.
}
return nil
}
func handleRd(cpu *CpuScalars, registers *[32]uint32, storeReg uint32, val uint32, conditional bool) error {
if storeReg >= 32 {
panic("invalid register")
}
if storeReg != 0 && conditional {
registers[storeReg] = val
}
cpu.PC = cpu.NextPC
cpu.NextPC = cpu.NextPC + 4
return nil
}
...@@ -124,8 +124,8 @@ ...@@ -124,8 +124,8 @@
"sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f" "sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f"
}, },
"src/cannon/MIPS.sol": { "src/cannon/MIPS.sol": {
"initCodeHash": "0xf0fb656774ff761e2fd9adc523d4ee3dfad6fab63a37d4177543cfc98708b1be", "initCodeHash": "0x1742f31c43d067f94e669e50e632875ba2a2795127ea5bf429acf7ed39ddbc48",
"sourceCodeHash": "0xe6a817aac69c149c24a9ab820853aae3c189eba337ab2e5c75a7cf458b45e308" "sourceCodeHash": "0xa50b47ddaee92c52c5f7cfb4b526f9d734c2eec524e7bb609b991d608f124c02"
}, },
"src/cannon/PreimageOracle.sol": { "src/cannon/PreimageOracle.sol": {
"initCodeHash": "0xe5db668fe41436f53995e910488c7c140766ba8745e19743773ebab508efd090", "initCodeHash": "0xe5db668fe41436f53995e910488c7c140766ba8745e19743773ebab508efd090",
......
...@@ -46,7 +46,7 @@ contract MIPS is ISemver { ...@@ -46,7 +46,7 @@ contract MIPS is ISemver {
/// @notice The semantic version of the MIPS contract. /// @notice The semantic version of the MIPS contract.
/// @custom:semver 1.0.1 /// @custom:semver 1.0.1
string public constant version = "1.1.0-beta.2"; string public constant version = "1.1.0-beta.3";
uint32 internal constant FD_STDIN = 0; uint32 internal constant FD_STDIN = 0;
uint32 internal constant FD_STDOUT = 1; uint32 internal constant FD_STDOUT = 1;
...@@ -302,146 +302,6 @@ contract MIPS is ISemver { ...@@ -302,146 +302,6 @@ contract MIPS is ISemver {
} }
} }
/// @notice Handles HI and LO register instructions.
/// @param _func 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.
/// @return out_ The hashed MIPS state.
function handleHiLo(uint32 _func, uint32 _rs, uint32 _rt, uint32 _storeReg) internal returns (bytes32 out_) {
unchecked {
// Load state from memory
State memory state;
assembly {
state := 0x80
}
uint32 val;
// mfhi: Move the contents of the HI register into the destination
if (_func == 0x10) {
val = state.hi;
}
// mthi: Move the contents of the source into the HI register
else if (_func == 0x11) {
state.hi = _rs;
}
// mflo: Move the contents of the LO register into the destination
else if (_func == 0x12) {
val = state.lo;
}
// mtlo: Move the contents of the source into the LO register
else if (_func == 0x13) {
state.lo = _rs;
}
// mult: Multiplies `rs` by `rt` and stores the result in HI and LO registers
else if (_func == 0x18) {
uint64 acc = uint64(int64(int32(_rs)) * int64(int32(_rt)));
state.hi = uint32(acc >> 32);
state.lo = uint32(acc);
}
// multu: Unsigned multiplies `rs` by `rt` and stores the result in HI and LO registers
else if (_func == 0x19) {
uint64 acc = uint64(uint64(_rs) * uint64(_rt));
state.hi = uint32(acc >> 32);
state.lo = uint32(acc);
}
// div: Divides `rs` by `rt`.
// Stores the quotient in LO
// And the remainder in HI
else if (_func == 0x1a) {
if (int32(_rt) == 0) {
revert("MIPS: division by zero");
}
state.hi = uint32(int32(_rs) % int32(_rt));
state.lo = uint32(int32(_rs) / int32(_rt));
}
// divu: Unsigned divides `rs` by `rt`.
// Stores the quotient in LO
// And the remainder in HI
else if (_func == 0x1b) {
if (_rt == 0) {
revert("MIPS: division by zero");
}
state.hi = _rs % _rt;
state.lo = _rs / _rt;
}
// Store the result in the destination register, if applicable
if (_storeReg != 0) {
state.registers[_storeReg] = val;
}
// Update the PC
state.pc = state.nextPC;
state.nextPC = state.nextPC + 4;
// Return the hash of the resulting state
out_ = outputState();
}
}
/// @notice Handles a jump instruction, updating the MIPS state PC where needed.
/// @param _linkReg The register to store the link to the instruction after the delay slot instruction.
/// @param _dest The destination to jump to.
/// @return out_ The hashed MIPS state.
function handleJump(uint32 _linkReg, uint32 _dest) internal returns (bytes32 out_) {
unchecked {
// Load state from memory.
State memory state;
assembly {
state := 0x80
}
if (state.nextPC != state.pc + 4) {
revert("jump in delay slot");
}
// Update the next PC to the jump destination.
uint32 prevPC = state.pc;
state.pc = state.nextPC;
state.nextPC = _dest;
// Update the link-register to the instruction after the delay slot instruction.
if (_linkReg != 0) {
state.registers[_linkReg] = prevPC + 8;
}
// Return the hash of the resulting state.
out_ = outputState();
}
}
/// @notice Handles a storing a value into a register.
/// @param _storeReg The register to store the value into.
/// @param _val The value to store.
/// @param _conditional Whether or not the store is conditional.
/// @return out_ The hashed MIPS state.
function handleRd(uint32 _storeReg, uint32 _val, bool _conditional) internal returns (bytes32 out_) {
unchecked {
// Load state from memory.
State memory state;
assembly {
state := 0x80
}
// The destination register must be valid.
require(_storeReg < 32, "valid register");
// Never write to reg 0, and it can be conditional (movz, movn).
if (_storeReg != 0 && _conditional) {
state.registers[_storeReg] = _val;
}
// Update the PC.
state.pc = state.nextPC;
state.nextPC = state.nextPC + 4;
// Return the hash of the resulting state.
out_ = outputState();
}
}
/// @notice Computes the offset of the proof in the calldata. /// @notice Computes the offset of the proof in the calldata.
/// @param _proofIndex The index of the proof in the calldata. /// @param _proofIndex The index of the proof in the calldata.
/// @return offset_ The offset of the proof in the calldata. /// @return offset_ The offset of the proof in the calldata.
...@@ -632,7 +492,7 @@ contract MIPS is ISemver { ...@@ -632,7 +492,7 @@ contract MIPS is ISemver {
if (opcode == 2 || opcode == 3) { if (opcode == 2 || opcode == 3) {
// Take top 4 bits of the next PC (its 256 MB region), and concatenate with the 26-bit offset // 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; uint32 target = (state.nextPC & 0xF0000000) | (insn & 0x03FFFFFF) << 2;
return handleJump(opcode == 2 ? 0 : 31, target); return handleJumpAndReturnOutput(state, opcode == 2 ? 0 : 31, target);
} }
// register fetch // register fetch
...@@ -707,16 +567,16 @@ contract MIPS is ISemver { ...@@ -707,16 +567,16 @@ contract MIPS is ISemver {
if (opcode == 0 && func >= 8 && func < 0x1c) { if (opcode == 0 && func >= 8 && func < 0x1c) {
if (func == 8 || func == 9) { if (func == 8 || func == 9) {
// jr/jalr // jr/jalr
return handleJump(func == 8 ? 0 : rdReg, rs); return handleJumpAndReturnOutput(state, func == 8 ? 0 : rdReg, rs);
} }
if (func == 0xa) { if (func == 0xa) {
// movz // movz
return handleRd(rdReg, rs, rt == 0); return handleRdAndReturnOutput(state, rdReg, rs, rt == 0);
} }
if (func == 0xb) { if (func == 0xb) {
// movn // movn
return handleRd(rdReg, rs, rt != 0); return handleRdAndReturnOutput(state, rdReg, rs, rt != 0);
} }
// syscall (can read and write) // syscall (can read and write)
...@@ -727,7 +587,19 @@ contract MIPS is ISemver { ...@@ -727,7 +587,19 @@ contract MIPS is ISemver {
// lo and hi registers // lo and hi registers
// can write back // can write back
if (func >= 0x10 && func < 0x1c) { if (func >= 0x10 && func < 0x1c) {
return handleHiLo(func, rs, rt, rdReg); st.CpuScalars memory cpu = getCpuScalars(state);
ins.handleHiLo({
_cpu: cpu,
_registers: state.registers,
_func: func,
_rs: rs,
_rt: rt,
_storeReg: rdReg
});
setStateCpuScalars(state, cpu);
return outputState();
} }
} }
...@@ -742,10 +614,49 @@ contract MIPS is ISemver { ...@@ -742,10 +614,49 @@ contract MIPS is ISemver {
} }
// write back the value to destination register // write back the value to destination register
return handleRd(rdReg, val, true); 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) { function getCpuScalars(State memory _state) internal pure returns (st.CpuScalars memory) {
return st.CpuScalars({ pc: _state.pc, nextPC: _state.nextPC, lo: _state.lo, hi: _state.hi }); return st.CpuScalars({ pc: _state.pc, nextPC: _state.nextPC, lo: _state.lo, hi: _state.hi });
} }
......
...@@ -341,4 +341,147 @@ library MIPSInstructions { ...@@ -341,4 +341,147 @@ 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 _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 _rs,
uint32 _rt,
uint32 _storeReg
)
internal
pure
{
unchecked {
uint32 val = 0;
// mfhi: Move the contents of the HI register into the destination
if (_func == 0x10) {
val = _cpu.hi;
}
// mthi: Move the contents of the source into the HI register
else if (_func == 0x11) {
_cpu.hi = _rs;
}
// mflo: Move the contents of the LO register into the destination
else if (_func == 0x12) {
val = _cpu.lo;
}
// mtlo: Move the contents of the source into the LO register
else if (_func == 0x13) {
_cpu.lo = _rs;
}
// mult: Multiplies `rs` by `rt` and stores the result in HI and LO registers
else if (_func == 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) {
uint64 acc = uint64(uint64(_rs) * uint64(_rt));
_cpu.hi = uint32(acc >> 32);
_cpu.lo = uint32(acc);
}
// div: Divides `rs` by `rt`.
// Stores the quotient in LO
// And the remainder in HI
else if (_func == 0x1a) {
if (int32(_rt) == 0) {
revert("MIPS: division by zero");
}
_cpu.hi = uint32(int32(_rs) % int32(_rt));
_cpu.lo = uint32(int32(_rs) / int32(_rt));
}
// divu: Unsigned divides `rs` by `rt`.
// Stores the quotient in LO
// And the remainder in HI
else if (_func == 0x1b) {
if (_rt == 0) {
revert("MIPS: division by zero");
}
_cpu.hi = _rs % _rt;
_cpu.lo = _rs / _rt;
}
// Store the result in the destination register, if applicable
if (_storeReg != 0) {
_registers[_storeReg] = val;
}
// Update the PC
_cpu.pc = _cpu.nextPC;
_cpu.nextPC = _cpu.nextPC + 4;
}
}
/// @notice Handles a jump instruction, updating the MIPS state PC where needed.
/// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo.
/// @param _registers Holds the current state of the cpu registers.
/// @param _linkReg The register to store the link to the instruction after the delay slot instruction.
/// @param _dest The destination to jump to.
function handleJump(
st.CpuScalars memory _cpu,
uint32[32] memory _registers,
uint32 _linkReg,
uint32 _dest
)
internal
pure
{
unchecked {
if (_cpu.nextPC != _cpu.pc + 4) {
revert("jump in delay slot");
}
// Update the next PC to the jump destination.
uint32 prevPC = _cpu.pc;
_cpu.pc = _cpu.nextPC;
_cpu.nextPC = _dest;
// Update the link-register to the instruction after the delay slot instruction.
if (_linkReg != 0) {
_registers[_linkReg] = prevPC + 8;
}
}
}
/// @notice Handles a storing a value into a register.
/// @param _cpu Holds the state of cpu scalars pc, nextPC, hi, lo.
/// @param _registers Holds the current state of the cpu registers.
/// @param _storeReg The register to store the value into.
/// @param _val The value to store.
/// @param _conditional Whether or not the store is conditional.
function handleRd(
st.CpuScalars memory _cpu,
uint32[32] memory _registers,
uint32 _storeReg,
uint32 _val,
bool _conditional
)
internal
pure
{
unchecked {
// The destination register must be valid.
require(_storeReg < 32, "valid register");
// Never write to reg 0, and it can be conditional (movz, movn).
if (_storeReg != 0 && _conditional) {
_registers[_storeReg] = _val;
}
// Update the PC.
_cpu.pc = _cpu.nextPC;
_cpu.nextPC = _cpu.nextPC + 4;
}
}
} }
...@@ -25,7 +25,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -25,7 +25,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865; address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865;
address internal constant l1StandardBridgeAddress = 0xb7900B27Be8f0E0fF65d1C3A4671e1220437dd2b; address internal constant l1StandardBridgeAddress = 0xb7900B27Be8f0E0fF65d1C3A4671e1220437dd2b;
address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F; address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F;
address internal constant mipsAddress = 0x477508A9612B67709Caf812D4356d531ba6a471d; address internal constant mipsAddress = 0x49E77cdE01fFBAB1266813b9a2FaADc1B757F435;
address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535; address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535;
address internal constant optimismPortalProxyAddress = 0x978e3286EB805934215a88694d80b09aDed68D90; address internal constant optimismPortalProxyAddress = 0x978e3286EB805934215a88694d80b09aDed68D90;
address internal constant preimageOracleAddress = 0x3bd7E801E51d48c5d94Ea68e8B801DFFC275De75; 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