Commit 4dd96e0b authored by mbaxter's avatar mbaxter Committed by GitHub

cannon: Extract MIPS execute, signExtend logic from mips.{go,sol} (#10759)

* cannon: Extract MIPS execute logic from mips.{go,sol}

* cannon: Add compiler version and license to MIPSInstruction.sol

* cannnon: Remove visibility modifier from library function

* cannon: Extract mips sign extend functions

* cannon: Move MIPS.sol comment to avoid line break, clarify comment

* cannon: Run `pnpm lint:fix`

* cannon: Run `pnpm semver-lock`

* cannon: Run `pnpm snapshots`

* cannon: Use namespace import for MIPSInstructions
parent 384e2dc7
...@@ -249,7 +249,7 @@ func (m *InstrumentedState) handleBranch(opcode uint32, insn uint32, rtReg uint3 ...@@ -249,7 +249,7 @@ func (m *InstrumentedState) handleBranch(opcode uint32, insn uint32, rtReg uint3
prevPC := m.state.PC prevPC := m.state.PC
m.state.PC = m.state.NextPC // execute the delay slot first m.state.PC = m.state.NextPC // execute the delay slot first
if shouldBranch { if shouldBranch {
m.state.NextPC = prevPC + 4 + (SE(insn&0xFFFF, 16) << 2) // then continue with the instruction the branch jumps to. m.state.NextPC = prevPC + 4 + (signExtend(insn&0xFFFF, 16) << 2) // then continue with the instruction the branch jumps to.
} else { } else {
m.state.NextPC = m.state.NextPC + 4 // branch not taken m.state.NextPC = m.state.NextPC + 4 // branch not taken
} }
...@@ -358,7 +358,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -358,7 +358,7 @@ func (m *InstrumentedState) mipsStep() error {
rt = insn & 0xFFFF rt = insn & 0xFFFF
} else { } else {
// SignExtImm // SignExtImm
rt = SE(insn&0xFFFF, 16) rt = signExtend(insn&0xFFFF, 16)
} }
} else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 { } else if opcode >= 0x28 || opcode == 0x22 || opcode == 0x26 {
// store rt value with store // store rt value with store
...@@ -378,7 +378,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -378,7 +378,7 @@ func (m *InstrumentedState) mipsStep() error {
mem := uint32(0) mem := uint32(0)
if opcode >= 0x20 { if opcode >= 0x20 {
// M[R[rs]+SignExtImm] // M[R[rs]+SignExtImm]
rs += SE(insn&0xFFFF, 16) rs += signExtend(insn&0xFFFF, 16)
addr := rs & 0xFFFFFFFC addr := rs & 0xFFFFFFFC
m.trackMemAccess(addr) m.trackMemAccess(addr)
mem = m.state.Memory.GetMemory(addr) mem = m.state.Memory.GetMemory(addr)
...@@ -391,7 +391,7 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -391,7 +391,7 @@ func (m *InstrumentedState) mipsStep() error {
} }
// ALU // ALU
val := execute(insn, rs, rt, mem) val := executeMipsInstruction(insn, rs, rt, mem)
fun := insn & 0x3f // 6-bits fun := insn & 0x3f // 6-bits
if opcode == 0 && fun >= 8 && fun < 0x1c { if opcode == 0 && fun >= 8 && fun < 0x1c {
...@@ -437,178 +437,3 @@ func (m *InstrumentedState) mipsStep() error { ...@@ -437,178 +437,3 @@ 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 m.handleRd(rdReg, val, true)
} }
func execute(insn uint32, rs uint32, rt uint32, mem uint32) uint32 {
opcode := insn >> 26 // 6-bits
if opcode == 0 || (opcode >= 8 && opcode < 0xF) {
fun := insn & 0x3f // 6-bits
// transform ArithLogI to SPECIAL
switch opcode {
case 8:
fun = 0x20 // addi
case 9:
fun = 0x21 // addiu
case 0xA:
fun = 0x2A // slti
case 0xB:
fun = 0x2B // sltiu
case 0xC:
fun = 0x24 // andi
case 0xD:
fun = 0x25 // ori
case 0xE:
fun = 0x26 // xori
}
switch fun {
case 0x00: // sll
return rt << ((insn >> 6) & 0x1F)
case 0x02: // srl
return rt >> ((insn >> 6) & 0x1F)
case 0x03: // sra
shamt := (insn >> 6) & 0x1F
return SE(rt>>shamt, 32-shamt)
case 0x04: // sllv
return rt << (rs & 0x1F)
case 0x06: // srlv
return rt >> (rs & 0x1F)
case 0x07: // srav
return SE(rt>>rs, 32-rs)
// functs in range [0x8, 0x1b] are handled specially by other functions
case 0x08: // jr
return rs
case 0x09: // jalr
return rs
case 0x0a: // movz
return rs
case 0x0b: // movn
return rs
case 0x0c: // syscall
return rs
// 0x0d - break not supported
case 0x0f: // sync
return rs
case 0x10: // mfhi
return rs
case 0x11: // mthi
return rs
case 0x12: // mflo
return rs
case 0x13: // mtlo
return rs
case 0x18: // mult
return rs
case 0x19: // multu
return rs
case 0x1a: // div
return rs
case 0x1b: // divu
return rs
// The rest includes transformed R-type arith imm instructions
case 0x20: // add
return rs + rt
case 0x21: // addu
return rs + rt
case 0x22: // sub
return rs - rt
case 0x23: // subu
return rs - rt
case 0x24: // and
return rs & rt
case 0x25: // or
return rs | rt
case 0x26: // xor
return rs ^ rt
case 0x27: // nor
return ^(rs | rt)
case 0x2a: // slti
if int32(rs) < int32(rt) {
return 1
}
return 0
case 0x2b: // sltiu
if rs < rt {
return 1
}
return 0
default:
panic("invalid instruction")
}
} else {
switch opcode {
// SPECIAL2
case 0x1C:
fun := insn & 0x3f // 6-bits
switch fun {
case 0x2: // mul
return uint32(int32(rs) * int32(rt))
case 0x20, 0x21: // clz, clo
if fun == 0x20 {
rs = ^rs
}
i := uint32(0)
for ; rs&0x80000000 != 0; i++ {
rs <<= 1
}
return i
}
case 0x0F: // lui
return rt << 16
case 0x20: // lb
return SE((mem>>(24-(rs&3)*8))&0xFF, 8)
case 0x21: // lh
return SE((mem>>(16-(rs&2)*8))&0xFFFF, 16)
case 0x22: // lwl
val := mem << ((rs & 3) * 8)
mask := uint32(0xFFFFFFFF) << ((rs & 3) * 8)
return (rt & ^mask) | val
case 0x23: // lw
return mem
case 0x24: // lbu
return (mem >> (24 - (rs&3)*8)) & 0xFF
case 0x25: // lhu
return (mem >> (16 - (rs&2)*8)) & 0xFFFF
case 0x26: // lwr
val := mem >> (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)
return (rt & ^mask) | val
case 0x28: // sb
val := (rt & 0xFF) << (24 - (rs&3)*8)
mask := 0xFFFFFFFF ^ uint32(0xFF<<(24-(rs&3)*8))
return (mem & mask) | val
case 0x29: // sh
val := (rt & 0xFFFF) << (16 - (rs&2)*8)
mask := 0xFFFFFFFF ^ uint32(0xFFFF<<(16-(rs&2)*8))
return (mem & mask) | val
case 0x2a: // swl
val := rt >> ((rs & 3) * 8)
mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8)
return (mem & ^mask) | val
case 0x2b: // sw
return rt
case 0x2e: // swr
val := rt << (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8)
return (mem & ^mask) | val
case 0x30: // ll
return mem
case 0x38: // sc
return rt
default:
panic("invalid instruction")
}
}
panic("invalid instruction")
}
func SE(dat uint32, idx uint32) uint32 {
isSigned := (dat >> (idx - 1)) != 0
signed := ((uint32(1) << (32 - idx)) - 1) << idx
mask := (uint32(1) << idx) - 1
if isSigned {
return dat&mask | signed
} else {
return dat & mask
}
}
package mipsevm
func executeMipsInstruction(insn uint32, rs uint32, rt uint32, mem uint32) uint32 {
opcode := insn >> 26 // 6-bits
if opcode == 0 || (opcode >= 8 && opcode < 0xF) {
fun := insn & 0x3f // 6-bits
// transform ArithLogI to SPECIAL
switch opcode {
case 8:
fun = 0x20 // addi
case 9:
fun = 0x21 // addiu
case 0xA:
fun = 0x2A // slti
case 0xB:
fun = 0x2B // sltiu
case 0xC:
fun = 0x24 // andi
case 0xD:
fun = 0x25 // ori
case 0xE:
fun = 0x26 // xori
}
switch fun {
case 0x00: // sll
return rt << ((insn >> 6) & 0x1F)
case 0x02: // srl
return rt >> ((insn >> 6) & 0x1F)
case 0x03: // sra
shamt := (insn >> 6) & 0x1F
return signExtend(rt>>shamt, 32-shamt)
case 0x04: // sllv
return rt << (rs & 0x1F)
case 0x06: // srlv
return rt >> (rs & 0x1F)
case 0x07: // srav
return signExtend(rt>>rs, 32-rs)
// functs in range [0x8, 0x1b] are handled specially by other functions
case 0x08: // jr
return rs
case 0x09: // jalr
return rs
case 0x0a: // movz
return rs
case 0x0b: // movn
return rs
case 0x0c: // syscall
return rs
// 0x0d - break not supported
case 0x0f: // sync
return rs
case 0x10: // mfhi
return rs
case 0x11: // mthi
return rs
case 0x12: // mflo
return rs
case 0x13: // mtlo
return rs
case 0x18: // mult
return rs
case 0x19: // multu
return rs
case 0x1a: // div
return rs
case 0x1b: // divu
return rs
// The rest includes transformed R-type arith imm instructions
case 0x20: // add
return rs + rt
case 0x21: // addu
return rs + rt
case 0x22: // sub
return rs - rt
case 0x23: // subu
return rs - rt
case 0x24: // and
return rs & rt
case 0x25: // or
return rs | rt
case 0x26: // xor
return rs ^ rt
case 0x27: // nor
return ^(rs | rt)
case 0x2a: // slti
if int32(rs) < int32(rt) {
return 1
}
return 0
case 0x2b: // sltiu
if rs < rt {
return 1
}
return 0
default:
panic("invalid instruction")
}
} else {
switch opcode {
// SPECIAL2
case 0x1C:
fun := insn & 0x3f // 6-bits
switch fun {
case 0x2: // mul
return uint32(int32(rs) * int32(rt))
case 0x20, 0x21: // clz, clo
if fun == 0x20 {
rs = ^rs
}
i := uint32(0)
for ; rs&0x80000000 != 0; i++ {
rs <<= 1
}
return i
}
case 0x0F: // lui
return rt << 16
case 0x20: // lb
return signExtend((mem>>(24-(rs&3)*8))&0xFF, 8)
case 0x21: // lh
return signExtend((mem>>(16-(rs&2)*8))&0xFFFF, 16)
case 0x22: // lwl
val := mem << ((rs & 3) * 8)
mask := uint32(0xFFFFFFFF) << ((rs & 3) * 8)
return (rt & ^mask) | val
case 0x23: // lw
return mem
case 0x24: // lbu
return (mem >> (24 - (rs&3)*8)) & 0xFF
case 0x25: // lhu
return (mem >> (16 - (rs&2)*8)) & 0xFFFF
case 0x26: // lwr
val := mem >> (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) >> (24 - (rs&3)*8)
return (rt & ^mask) | val
case 0x28: // sb
val := (rt & 0xFF) << (24 - (rs&3)*8)
mask := 0xFFFFFFFF ^ uint32(0xFF<<(24-(rs&3)*8))
return (mem & mask) | val
case 0x29: // sh
val := (rt & 0xFFFF) << (16 - (rs&2)*8)
mask := 0xFFFFFFFF ^ uint32(0xFFFF<<(16-(rs&2)*8))
return (mem & mask) | val
case 0x2a: // swl
val := rt >> ((rs & 3) * 8)
mask := uint32(0xFFFFFFFF) >> ((rs & 3) * 8)
return (mem & ^mask) | val
case 0x2b: // sw
return rt
case 0x2e: // swr
val := rt << (24 - (rs&3)*8)
mask := uint32(0xFFFFFFFF) << (24 - (rs&3)*8)
return (mem & ^mask) | val
case 0x30: // ll
return mem
case 0x38: // sc
return rt
default:
panic("invalid instruction")
}
}
panic("invalid instruction")
}
func signExtend(dat uint32, idx uint32) uint32 {
isSigned := (dat >> (idx - 1)) != 0
signed := ((uint32(1) << (32 - idx)) - 1) << idx
mask := (uint32(1) << idx) - 1
if isSigned {
return dat&mask | signed
} else {
return dat & mask
}
}
...@@ -124,8 +124,8 @@ ...@@ -124,8 +124,8 @@
"sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f" "sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f"
}, },
"src/cannon/MIPS.sol": { "src/cannon/MIPS.sol": {
"initCodeHash": "0xa5d36fc67170ad87322f358f612695f642757bbf5280800d5d878da21402579a", "initCodeHash": "0x1c5dbe83af31e70feb906e2bda2bb1d78d3d15012ec6b11ba5643785657af2a6",
"sourceCodeHash": "0x75701f3efb7a9c16079ba0a4ed2867999aab7d95bfa0fe5ebb131cfc278593aa" "sourceCodeHash": "0x9bdc97ff4e51fdec7c3e2113d5b60cd64eeb121a51122bea972789d4a5ac3dfa"
}, },
"src/cannon/PreimageOracle.sol": { "src/cannon/PreimageOracle.sol": {
"initCodeHash": "0xe5db668fe41436f53995e910488c7c140766ba8745e19743773ebab508efd090", "initCodeHash": "0xe5db668fe41436f53995e910488c7c140766ba8745e19743773ebab508efd090",
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
/// @notice Execute an instruction.
function executeMipsInstruction(uint32 insn, uint32 rs, uint32 rt, uint32 mem) pure returns (uint32 out) {
unchecked {
uint32 opcode = insn >> 26; // 6-bits
if (opcode == 0 || (opcode >= 8 && opcode < 0xF)) {
uint32 func = insn & 0x3f; // 6-bits
assembly {
// transform ArithLogI to SPECIAL
switch opcode
// addi
case 0x8 { func := 0x20 }
// addiu
case 0x9 { func := 0x21 }
// stli
case 0xA { func := 0x2A }
// sltiu
case 0xB { func := 0x2B }
// andi
case 0xC { func := 0x24 }
// ori
case 0xD { func := 0x25 }
// xori
case 0xE { func := 0x26 }
}
// sll
if (func == 0x00) {
return rt << ((insn >> 6) & 0x1F);
}
// srl
else if (func == 0x02) {
return rt >> ((insn >> 6) & 0x1F);
}
// sra
else if (func == 0x03) {
uint32 shamt = (insn >> 6) & 0x1F;
return signExtend(rt >> shamt, 32 - shamt);
}
// sllv
else if (func == 0x04) {
return rt << (rs & 0x1F);
}
// srlv
else if (func == 0x6) {
return rt >> (rs & 0x1F);
}
// srav
else if (func == 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) {
return rs;
}
// jalr
else if (func == 0x09) {
return rs;
}
// movz
else if (func == 0x0a) {
return rs;
}
// movn
else if (func == 0x0b) {
return rs;
}
// syscall
else if (func == 0x0c) {
return rs;
}
// 0x0d - break not supported
// sync
else if (func == 0x0f) {
return rs;
}
// mfhi
else if (func == 0x10) {
return rs;
}
// mthi
else if (func == 0x11) {
return rs;
}
// mflo
else if (func == 0x12) {
return rs;
}
// mtlo
else if (func == 0x13) {
return rs;
}
// mult
else if (func == 0x18) {
return rs;
}
// multu
else if (func == 0x19) {
return rs;
}
// div
else if (func == 0x1a) {
return rs;
}
// divu
else if (func == 0x1b) {
return rs;
}
// The rest includes transformed R-type arith imm instructions
// add
else if (func == 0x20) {
return (rs + rt);
}
// addu
else if (func == 0x21) {
return (rs + rt);
}
// sub
else if (func == 0x22) {
return (rs - rt);
}
// subu
else if (func == 0x23) {
return (rs - rt);
}
// and
else if (func == 0x24) {
return (rs & rt);
}
// or
else if (func == 0x25) {
return (rs | rt);
}
// xor
else if (func == 0x26) {
return (rs ^ rt);
}
// nor
else if (func == 0x27) {
return ~(rs | rt);
}
// slti
else if (func == 0x2a) {
return int32(rs) < int32(rt) ? 1 : 0;
}
// sltiu
else if (func == 0x2b) {
return rs < rt ? 1 : 0;
} else {
revert("invalid instruction");
}
} else {
// SPECIAL2
if (opcode == 0x1C) {
uint32 func = insn & 0x3f; // 6-bits
// mul
if (func == 0x2) {
return uint32(int32(rs) * int32(rt));
}
// clz, clo
else if (func == 0x20 || func == 0x21) {
if (func == 0x20) {
rs = ~rs;
}
uint32 i = 0;
while (rs & 0x80000000 != 0) {
i++;
rs <<= 1;
}
return i;
}
}
// lui
else if (opcode == 0x0F) {
return rt << 16;
}
// lb
else if (opcode == 0x20) {
return signExtend((mem >> (24 - (rs & 3) * 8)) & 0xFF, 8);
}
// lh
else if (opcode == 0x21) {
return signExtend((mem >> (16 - (rs & 2) * 8)) & 0xFFFF, 16);
}
// lwl
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) {
return mem;
}
// lbu
else if (opcode == 0x24) {
return (mem >> (24 - (rs & 3) * 8)) & 0xFF;
}
// lhu
else if (opcode == 0x25) {
return (mem >> (16 - (rs & 2) * 8)) & 0xFFFF;
}
// lwr
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) {
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) {
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) {
uint32 val = rt >> ((rs & 3) * 8);
uint32 mask = uint32(0xFFFFFFFF) >> ((rs & 3) * 8);
return (mem & ~mask) | val;
}
// sw
else if (opcode == 0x2b) {
return rt;
}
// swr
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) {
return mem;
}
// sc
else if (opcode == 0x38) {
return rt;
} else {
revert("invalid instruction");
}
}
revert("invalid instruction");
}
}
/// @notice Extends the value leftwards with its most significant bit (sign extension).
function signExtend(uint32 _dat, uint32 _idx) pure returns (uint32 out_) {
unchecked {
bool isSigned = (_dat >> (_idx - 1)) != 0;
uint256 signed = ((1 << (32 - _idx)) - 1) << _idx;
uint256 mask = (1 << _idx) - 1;
return uint32(_dat & mask | (isSigned ? signed : 0));
}
}
...@@ -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 = 0xF698388BFCDbd3f9f2F13ebC3E01471B3cc7cE83; address internal constant mipsAddress = 0x1C0e3B8e58dd91536Caf37a6009536255A7816a6;
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