From bf5a7c5aad631f33b8e0305869425230bd75035e Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Thu, 13 Feb 2025 14:30:59 -0500 Subject: [PATCH 01/10] use uints --- cannon/mipsevm/memory/memory.go | 14 +++++++-- cannon/mipsevm/memory/page.go | 50 +++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index e1df7b218e4fe..c84920e8ba97b 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -151,13 +151,17 @@ func (m *Memory) SetWord(addr Word, v Word) { // Go may mmap relatively large ranges, but we only allocate the pages just in time. p = m.AllocPage(pageIndex) } else { - prevValid := p.Ok[1] + prevValid := p.getBit(1) p.invalidate(pageAddr) if prevValid { m.merkleIndex.Invalidate(addr) // invalidate this branch of memory, now that the value changed } } - arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes], v) + arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes:pageAddr+arch.WordSizeBytes], v) + // // Manually inline arch.ByteOrderWord.PutWord to eliminate slice bounds check + // data := p.Data // Local copy hints to the compiler that it doesn't need to re-check bounds + // _ = data[pageAddr+arch.WordSizeBytes-1] // Bounds check elimination trick + // arch.ByteOrderWord.PutWord(data[pageAddr:pageAddr+arch.WordSizeBytes], v) } // GetWord reads the maximum sized value, [arch.Word], located at the specified address. @@ -173,7 +177,11 @@ func (m *Memory) GetWord(addr Word) Word { return 0 } pageAddr := addr & PageAddrMask - return arch.ByteOrderWord.Word(p.Data[pageAddr : pageAddr+arch.WordSizeBytes]) + // Manually inline arch.ByteOrderWord.PutWord to eliminate slice bounds check + // data := p.Data // Local copy hints to the compiler that it doesn't need to re-check bounds + // _ = data[pageAddr+arch.WordSizeBytes-1] // Bounds check elimination trick + // return arch.ByteOrderWord.Word(data[pageAddr : pageAddr+arch.WordSizeBytes : pageAddr+arch.WordSizeBytes]) + return arch.ByteOrderWord.Word(p.Data[pageAddr : pageAddr+arch.WordSizeBytes : pageAddr+arch.WordSizeBytes]) } func (m *Memory) AllocPage(pageIndex Word) *CachedPage { diff --git a/cannon/mipsevm/memory/page.go b/cannon/mipsevm/memory/page.go index f87adbb1ef8ef..950ae16bb98d2 100644 --- a/cannon/mipsevm/memory/page.go +++ b/cannon/mipsevm/memory/page.go @@ -66,47 +66,73 @@ type CachedPage struct { Data *Page // intermediate nodes only Cache [PageSize / 32][32]byte - // true if the intermediate node is valid - Ok [PageSize / 32]bool + // bit set to 1 if the intermediate node is valid + OkLow, OkHigh uint64 // size is PageSize / 32 == 64 + 64 == 128 } func (p *CachedPage) invalidate(pageAddr Word) { if pageAddr >= PageSize { panic("invalid page addr") } - k := (1 << PageAddrSize) | pageAddr - // first cache layer caches nodes that has two 32 byte leaf nodes. + + k := uint64((1 << PageAddrSize) | pageAddr) k >>= 5 + 1 + for k > 0 { - p.Ok[k] = false + mask := uint64(1) << (k & 63) // Compute bit mask + isHigh := k >> 6 // 1 if k >= 64, 0 if k < 64 + p.OkLow &^= mask * (1 - isHigh) // Zero out bit in OkLow if isHigh == 0 + p.OkHigh &^= mask * isHigh // Zero out bit in OkHigh if isHigh == 1 + k >>= 1 } } +func (p *CachedPage) getBit(k uint64) bool { + if k < 64 { + return (p.OkLow & (1 << k)) != 0 + } + return (p.OkHigh & (1 << (k - 64))) != 0 +} + +func (p *CachedPage) setBit(k uint64) { + mask := uint64(1) << (k & 63) // Create bit mask + isHigh := k >> 6 // 1 if k >= 64, 0 if k < 64 + + // Branchless update + p.OkLow |= mask * (1 - isHigh) // Set bit in OkLow if isHigh == 0 + p.OkHigh |= mask * isHigh // Set bit in OkHigh if isHigh == 1 +} + func (p *CachedPage) InvalidateFull() { - p.Ok = [PageSize / 32]bool{} // reset everything to false + // p.Ok = [PageSize / 32]bool{} // reset everything to false + p.OkHigh = 0 + p.OkLow = 0 + // p.Ok = bits.NewBitlist64(PageSize / 32) } func (p *CachedPage) MerkleRoot() [32]byte { // hash the bottom layer for i := uint64(0); i < PageSize; i += 64 { j := PageSize/32/2 + i/64 - if p.Ok[j] { + if p.getBit(j) { continue } - p.Cache[j] = crypto.Keccak256Hash(p.Data[i : i+64]) + p.Cache[j] = crypto.Keccak256Hash(p.Data[i : i+64 : i+64]) //fmt.Printf("0x%x 0x%x -> 0x%x\n", p.Data[i:i+32], p.Data[i+32:i+64], p.Cache[j]) - p.Ok[j] = true + // p.Ok[j] = true + p.setBit(j) } // hash the cache layers for i := PageSize/32 - 2; i > 0; i -= 2 { j := i >> 1 - if p.Ok[j] { + if p.getBit(uint64(j)) { continue } p.Cache[j] = HashPair(p.Cache[i], p.Cache[i+1]) - p.Ok[j] = true + // p.Ok[j] = true + p.setBit(uint64(j)) } return p.Cache[1] @@ -120,7 +146,7 @@ func (p *CachedPage) MerkleizeSubtree(gindex uint64) [32]byte { } // it's pointing to a bottom node nodeIndex := gindex & (PageAddrMask >> 5) - return *(*[32]byte)(p.Data[nodeIndex*32 : nodeIndex*32+32]) + return *(*[32]byte)(p.Data[nodeIndex*32 : nodeIndex*32+32 : nodeIndex*32+32]) } return p.Cache[gindex] } From a0dd029ec91c81f191f0787a0823dd70637bb1cf Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Thu, 13 Feb 2025 18:05:46 -0500 Subject: [PATCH 02/10] hashpool --- cannon/mipsevm/memory/binary_tree.go | 70 ++++++++++++++++++++++++++-- cannon/mipsevm/memory/memory.go | 10 +--- cannon/mipsevm/memory/page.go | 8 ++-- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index aea5bfd7771b4..86f61a85e144c 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -2,8 +2,63 @@ package memory import ( "math/bits" + "sync" + + "github.com/ethereum/go-ethereum/crypto" ) +// Byte32Pool is a sync.Pool for [32]byte slices +var Byte32Pool = sync.Pool{ + New: func() interface{} { + var b [32]byte + return &b // Return a pointer to avoid extra allocations + }, +} + +// GetByte32 retrieves a *[32]byte from the pool +func GetByte32() *[32]byte { + return Byte32Pool.Get().(*[32]byte) +} + +// PutByte32 returns a *[32]byte to the pool +func PutByte32(b *[32]byte) { + // Optional: Zero the array before putting it back + *b = [32]byte{} + Byte32Pool.Put(b) +} + +var hashPool = sync.Pool{ + New: func() interface{} { + return crypto.NewKeccakState() + }, +} + +func GetHasher() crypto.KeccakState { + return hashPool.Get().(crypto.KeccakState) +} + +func PutHasher(h crypto.KeccakState) { + h.Reset() + hashPool.Put(h) +} + +func Hash2(out *[32]byte, left, right *[32]byte) { + h := GetHasher() + h.Write(left[:]) + h.Write(right[:]) + h.Read(out[:]) + PutHasher(h) +} + +func HashData(out *[32]byte, data ...[]byte) { + h := GetHasher() + for _, b := range data { + h.Write(b) + } + h.Read(out[:]) + PutHasher(h) +} + // BinaryTreeIndex is a representation of the state of the memory in a binary merkle tree. type BinaryTreeIndex struct { // generalized index -> merkle root or nil if invalidated @@ -43,6 +98,10 @@ func (m *BinaryTreeIndex) Invalidate(addr Word) { gindex := (uint64(1) << (WordSize - PageAddrSize)) | uint64(addr>>PageAddrSize) for gindex > 0 { + n := m.nodes[gindex] + if n != nil { + PutByte32(n) + } m.nodes[gindex] = nil gindex >>= 1 } @@ -73,9 +132,10 @@ func (m *BinaryTreeIndex) MerkleizeSubtree(gindex uint64) [32]byte { } left := m.MerkleizeSubtree(gindex << 1) right := m.MerkleizeSubtree((gindex << 1) | 1) - r := HashPair(left, right) - m.nodes[gindex] = &r - return r + r := GetByte32() + Hash2(r, &left, &right) + m.nodes[gindex] = r + return *r } func (m *BinaryTreeIndex) MerkleProof(addr Word) (out [MemProofSize]byte) { @@ -115,6 +175,10 @@ func (m *BinaryTreeIndex) AddPage(pageIndex Word) { // make nodes to root k := (1 << PageKeySize) | uint64(pageIndex) for k > 0 { + n := m.nodes[k] + if n != nil { + PutByte32(n) + } m.nodes[k] = nil k >>= 1 } diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index c84920e8ba97b..1d70f8aa6ca04 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -157,11 +157,7 @@ func (m *Memory) SetWord(addr Word, v Word) { m.merkleIndex.Invalidate(addr) // invalidate this branch of memory, now that the value changed } } - arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes:pageAddr+arch.WordSizeBytes], v) - // // Manually inline arch.ByteOrderWord.PutWord to eliminate slice bounds check - // data := p.Data // Local copy hints to the compiler that it doesn't need to re-check bounds - // _ = data[pageAddr+arch.WordSizeBytes-1] // Bounds check elimination trick - // arch.ByteOrderWord.PutWord(data[pageAddr:pageAddr+arch.WordSizeBytes], v) + arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes], v) } // GetWord reads the maximum sized value, [arch.Word], located at the specified address. @@ -177,10 +173,6 @@ func (m *Memory) GetWord(addr Word) Word { return 0 } pageAddr := addr & PageAddrMask - // Manually inline arch.ByteOrderWord.PutWord to eliminate slice bounds check - // data := p.Data // Local copy hints to the compiler that it doesn't need to re-check bounds - // _ = data[pageAddr+arch.WordSizeBytes-1] // Bounds check elimination trick - // return arch.ByteOrderWord.Word(data[pageAddr : pageAddr+arch.WordSizeBytes : pageAddr+arch.WordSizeBytes]) return arch.ByteOrderWord.Word(p.Data[pageAddr : pageAddr+arch.WordSizeBytes : pageAddr+arch.WordSizeBytes]) } diff --git a/cannon/mipsevm/memory/page.go b/cannon/mipsevm/memory/page.go index 950ae16bb98d2..3a4dc48028ecb 100644 --- a/cannon/mipsevm/memory/page.go +++ b/cannon/mipsevm/memory/page.go @@ -9,8 +9,6 @@ import ( "fmt" "io" "sync" - - "github.com/ethereum/go-ethereum/crypto" ) var zlibWriterPool = sync.Pool{ @@ -105,7 +103,6 @@ func (p *CachedPage) setBit(k uint64) { } func (p *CachedPage) InvalidateFull() { - // p.Ok = [PageSize / 32]bool{} // reset everything to false p.OkHigh = 0 p.OkLow = 0 // p.Ok = bits.NewBitlist64(PageSize / 32) @@ -118,7 +115,8 @@ func (p *CachedPage) MerkleRoot() [32]byte { if p.getBit(j) { continue } - p.Cache[j] = crypto.Keccak256Hash(p.Data[i : i+64 : i+64]) + HashData(&p.Cache[j], p.Data[i:i+64]) + // p.Cache[j] = crypto.Keccak256Hash(p.Data[i : i+64 : i+64]) //fmt.Printf("0x%x 0x%x -> 0x%x\n", p.Data[i:i+32], p.Data[i+32:i+64], p.Cache[j]) // p.Ok[j] = true p.setBit(j) @@ -130,7 +128,7 @@ func (p *CachedPage) MerkleRoot() [32]byte { if p.getBit(uint64(j)) { continue } - p.Cache[j] = HashPair(p.Cache[i], p.Cache[i+1]) + Hash2(&p.Cache[j], &p.Cache[i], &p.Cache[i+1]) // p.Ok[j] = true p.setBit(uint64(j)) } From 08078ac0cdf299dfc7e8efb212395fbff5a2f1c3 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 18 Feb 2025 13:51:18 -0500 Subject: [PATCH 03/10] alloc pages upfront --- cannon/cmd/run.go | 41 +++++++- cannon/mipsevm/arch/arch32.go | 10 +- cannon/mipsevm/arch/arch64.go | 11 +- cannon/mipsevm/exec/mips_syscalls.go | 9 +- cannon/mipsevm/memory/binary_tree.go | 10 +- cannon/mipsevm/memory/memory.go | 147 ++++++++++++++++++++++++--- cannon/mipsevm/memory/page.go | 10 +- cannon/mipsevm/memory/page_test.go | 2 +- cannon/mipsevm/multithreaded/mips.go | 30 ++++++ 9 files changed, 234 insertions(+), 36 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 3399c437f1daf..cc1b658eaa495 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -375,6 +375,7 @@ func Run(ctx *cli.Context) error { } state, err := versions.LoadStateFromFile(ctx.Path(RunInputFlag.Name)) + if err != nil { return fmt.Errorf("failed to load state: %w", err) } @@ -402,7 +403,18 @@ func Run(ctx *cli.Context) error { if po.cmd != nil { stepFn = Guard(po.cmd.ProcessState, stepFn) } - + fmt.Printf("Initial memory Range") + ranges := state.GetMemory().GetAllocatedRanges() + for i, r := range ranges { + l.Info("memory range", + "num", i, + "start", fmt.Sprintf("%#x", r[0]), + "end", fmt.Sprintf("%#x", r[1]), + "size", r[2], + "gap", r[3], + ) + } + state.GetMemory().Rejig() start := time.Now() startStep := state.GetStep() @@ -418,6 +430,33 @@ func Run(ctx *cli.Context) error { if infoAt(state) { delta := time.Since(start) pc := state.GetPC() + + // ranges := state.GetMemory().GetAllocatedRanges() + // for i, r := range ranges { + // l.Info("memory range", + // "num", i, + // "start", fmt.Sprintf("%#x", r[0]), + // "end", fmt.Sprintf("%#x", r[1]), + // "size", r[2], + // "gap", r[3], + // ) + // } + totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion + totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion + l.Info("Read Section Percentages", + "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), + "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), + "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), + "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), + ) + + l.Info("Write Section Percentages", + "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), + "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), + "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), + "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), + ) + insn := mipsexec.LoadSubWord(state.GetMemory(), pc, 4, false, new(mipsexec.NoopMemoryTracker)) l.Info("processing", "step", step, diff --git a/cannon/mipsevm/arch/arch32.go b/cannon/mipsevm/arch/arch32.go index 2d16ed3b84737..cf384710985ed 100644 --- a/cannon/mipsevm/arch/arch32.go +++ b/cannon/mipsevm/arch/arch32.go @@ -17,10 +17,12 @@ const ( WordSize = 32 ExtMask = 0x3 - HeapStart = 0x05_00_00_00 - HeapEnd = 0x60_00_00_00 - ProgramBreak = 0x40_00_00_00 - HighMemoryStart = 0x7f_ff_d0_00 + Limit = 0xff_ff_ff_ff + ProgramHeapStart = 0x00_c0_00_00 + HeapStart = 0x05_00_00_00 + HeapEnd = 0x60_00_00_00 + ProgramBreak = 0x40_00_00_00 + HighMemoryStart = 0x7f_ff_d0_00 ) // 32-bit Syscall codes diff --git a/cannon/mipsevm/arch/arch64.go b/cannon/mipsevm/arch/arch64.go index 9c70fb7ee8336..bd9da0338f5cd 100644 --- a/cannon/mipsevm/arch/arch64.go +++ b/cannon/mipsevm/arch/arch64.go @@ -18,11 +18,12 @@ const ( ExtMask = 0x7 // Ensure virtual address is limited to 48-bits as many user programs assume such to implement packed pointers - // limit 0x00_00_FF_FF_FF_FF_FF_FF - HeapStart = 0x00_00_10_00_00_00_00_00 - HeapEnd = 0x00_00_60_00_00_00_00_00 - ProgramBreak = 0x00_00_40_00_00_00_00_00 - HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00 + Limit = 0x00_00_FF_FF_FF_FF_FF_FF + ProgramHeapStart = 0x00_00_00_c0_00_00_00_00 + HeapStart = 0x00_00_10_00_00_00_00_00 + HeapEnd = 0x00_00_60_00_00_00_00_00 + ProgramBreak = 0x00_00_40_00_00_00_00_00 + HighMemoryStart = 0x00_00_7F_FF_FF_FF_F0_00 ) // MIPS64 syscall table - https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/arch/mips/kernel/syscalls/syscall_n64.tbl. Generate the syscall numbers using the Makefile in that directory. diff --git a/cannon/mipsevm/exec/mips_syscalls.go b/cannon/mipsevm/exec/mips_syscalls.go index ca0801d9d659f..701cc2a55b1c9 100644 --- a/cannon/mipsevm/exec/mips_syscalls.go +++ b/cannon/mipsevm/exec/mips_syscalls.go @@ -2,6 +2,7 @@ package exec import ( "encoding/binary" + "fmt" "io" "github.com/ethereum/go-ethereum/common" @@ -116,7 +117,9 @@ func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) { } if a0 == 0 { v0 = heap - //fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz) + v0bytes := make([]byte, 8) + binary.BigEndian.PutUint64(v0bytes, uint64(v0)) + fmt.Printf("mmap heap %x size 0x%x\n", v0bytes, sz) newHeap += sz // Fail if new heap exceeds memory limit, newHeap overflows around to low memory, or sz overflows if newHeap > program.HEAP_END || newHeap < heap || sz < a1 { @@ -126,7 +129,9 @@ func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) { } } else { v0 = a0 - //fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz) + v0bytes := make([]byte, 8) + binary.BigEndian.PutUint64(v0bytes, uint64(v0)) + fmt.Printf("mmap hint %x size 0x%x\n", v0bytes, sz) } return v0, v1, newHeap diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index 86f61a85e144c..f742868e9aebc 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -72,9 +72,10 @@ func NewBinaryTreeMemory() *Memory { index := NewBinaryTreeIndex() index.setPageBacking(pages) return &Memory{ - merkleIndex: index, - pageTable: pages, - lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + merkleIndex: index, + pageTable: pages, + lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + ProgramRegion: make([]*CachedPage, 0, 1<<31), } } @@ -141,7 +142,8 @@ func (m *BinaryTreeIndex) MerkleizeSubtree(gindex uint64) [32]byte { func (m *BinaryTreeIndex) MerkleProof(addr Word) (out [MemProofSize]byte) { proof := m.traverseBranch(1, addr, 0) // encode the proof - for i := 0; i < MemProofLeafCount; i++ { + _ = proof[MemProofLeafCount-1] + for i := 0; i <= MemProofLeafCount-1; i++ { copy(out[i*32:(i+1)*32], proof[i][:]) } return out diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 1d70f8aa6ca04..a52aeadef7c08 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -53,6 +53,18 @@ type Memory struct { // this prevents map lookups each instruction lastPageKeys [2]Word lastPage [2]*CachedPage + + ProgramRegion []*CachedPage + + RprogramRegion uint64 + RmallocRegion uint64 + RheapRegion uint64 + RstackRegion uint64 + + WprogramRegion uint64 + WmallocRegion uint64 + WheapRegion uint64 + WstackRegion uint64 } type PageIndex interface { @@ -66,6 +78,57 @@ type PageIndex interface { setPageBacking(pages map[Word]*CachedPage) } +func (m *Memory) Rejig() { + indexes := maps.Keys(m.pageTable) + slices.Sort(indexes) + for _, pageIdx := range indexes { + if pageIdx<>arch.PageAddrSize { + // fmt.Printf("PageLookup under heapstart: %x\n", pageIndex) + return m.ProgramRegion[pageIndex], true + } // hit caches if pageIndex == m.lastPageKeys[0] { return m.lastPage[0], true @@ -112,8 +178,19 @@ func (m *Memory) PageLookup(pageIndex Word) (*CachedPage, bool) { return p, ok } +func (m *Memory) Mmap(addr, v1, newheap Word) { + fmt.Printf("Mmap addr: %x v1: %x newheap: %x\n", addr, v1, newheap) +} + func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error { for { + if !arch.IsMips32 { + seg := addr >> (48 - 2) + if seg == 1 { + fmt.Printf("SetMemoryRange addr: %x seg: %d\n", addr, seg) + } + } + pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask readLen := PageSize - pageAddr @@ -142,7 +219,17 @@ func (m *Memory) SetWord(addr Word, v Word) { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - + if addr < arch.ProgramHeapStart { + m.WprogramRegion++ + } else if addr < arch.HeapStart { + m.WmallocRegion++ + } else if addr < arch.HighMemoryStart { + m.WheapRegion++ + } else if addr < arch.Limit { + m.WstackRegion++ + } else { + panic(fmt.Errorf("invalid memory write: %x", addr)) + } pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask p, ok := m.PageLookup(pageIndex) @@ -157,6 +244,7 @@ func (m *Memory) SetWord(addr Word, v Word) { m.merkleIndex.Invalidate(addr) // invalidate this branch of memory, now that the value changed } } + arch.ByteOrderWord.PutWord(p.Data[pageAddr:pageAddr+arch.WordSizeBytes], v) } @@ -167,7 +255,22 @@ func (m *Memory) GetWord(addr Word) Word { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } + if addr < arch.ProgramHeapStart { + m.RprogramRegion++ + } else if addr < arch.HeapStart { + m.RmallocRegion++ + } else if addr < arch.HighMemoryStart { + m.RheapRegion++ + } else if addr < arch.Limit { + m.RstackRegion++ + } else { + panic(fmt.Errorf("invalid memory write: %x", addr)) + } pageIndex := addr >> PageAddrSize + seg := addr >> (48 - 2) + if seg == 2 { + fmt.Printf("GetWord addr: %x seg: %d\n", addr, seg) + } p, ok := m.PageLookup(pageIndex) if !ok { return 0 @@ -177,7 +280,18 @@ func (m *Memory) GetWord(addr Word) Word { } func (m *Memory) AllocPage(pageIndex Word) *CachedPage { - p := &CachedPage{Data: new(Page)} + // fmt.Printf("AllocPage pageIndex: %x\n", pageIndex) + // fmt.Printf("ProgramRegion: %d\n", len(m.ProgramRegion)) + p := new(CachedPage) + if pageIndex < arch.ProgramHeapStart>>arch.PageAddrSize { + proglen := len(m.ProgramRegion) + if pageIndex >= Word(proglen) { + m.ProgramRegion = m.ProgramRegion[:pageIndex+1] + } + m.ProgramRegion[pageIndex] = p + } + + p.Data = make(Page, PageSize) m.pageTable[pageIndex] = p m.merkleIndex.AddPage(pageIndex) return p @@ -243,15 +357,17 @@ func (m *Memory) Copy() *Memory { table := m.merkleIndex.New() table.setPageBacking(pages) out := &Memory{ - merkleIndex: table, - pageTable: pages, - lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - lastPage: [2]*CachedPage{nil, nil}, + merkleIndex: table, + pageTable: pages, + lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + lastPage: [2]*CachedPage{nil, nil}, + ProgramRegion: make([]*CachedPage, len(m.ProgramRegion), 1<<31), } for k, page := range m.pageTable { - data := new(Page) - *data = *page.Data + data := make(Page, PageSize, PageSize) + // *data = *page.Data + copy(data, page.Data) out.AllocPage(k).Data = data } return out @@ -304,16 +420,18 @@ func (m *Memory) Deserialize(in io.Reader) error { } type pageEntry struct { - Index Word `json:"index"` - Data *Page `json:"data"` + Index Word `json:"index"` + Data *[PageSize]byte `json:"data"` } func (m *Memory) MarshalJSON() ([]byte, error) { // nosemgrep pages := make([]pageEntry, 0, len(m.pageTable)) for k, p := range m.pageTable { + data := new([PageSize]byte) + copy(data[:], p.Data) pages = append(pages, pageEntry{ Index: k, - Data: p.Data, + Data: data, }) } sort.Slice(pages, func(i, j int) bool { @@ -331,7 +449,8 @@ func (m *Memory) UnmarshalJSON(data []byte) error { if _, ok := m.pageTable[p.Index]; ok { return fmt.Errorf("cannot load duplicate page, entry %d, page index %d", i, p.Index) } - m.AllocPage(p.Index).Data = p.Data + page := m.AllocPage(p.Index) + copy(page.Data, p.Data[:]) } return nil } diff --git a/cannon/mipsevm/memory/page.go b/cannon/mipsevm/memory/page.go index 3a4dc48028ecb..cdde96b37e839 100644 --- a/cannon/mipsevm/memory/page.go +++ b/cannon/mipsevm/memory/page.go @@ -18,9 +18,9 @@ var zlibWriterPool = sync.Pool{ }, } -type Page [PageSize]byte +type Page []byte -func (p *Page) MarshalJSON() ([]byte, error) { // nosemgrep +func (p Page) MarshalJSON() ([]byte, error) { // nosemgrep var out bytes.Buffer w := zlibWriterPool.Get().(*zlib.Writer) defer zlibWriterPool.Put(w) @@ -34,7 +34,7 @@ func (p *Page) MarshalJSON() ([]byte, error) { // nosemgrep return json.Marshal(out.Bytes()) } -func (p *Page) UnmarshalJSON(dat []byte) error { +func (p Page) UnmarshalJSON(dat []byte) error { // Strip off the `"` characters at the start & end. dat = dat[1 : len(dat)-1] // Decode b64 then decompress @@ -52,7 +52,7 @@ func (p *Page) UnmarshalJSON(dat []byte) error { } } -func (p *Page) UnmarshalText(dat []byte) error { +func (p Page) UnmarshalText(dat []byte) error { if len(dat) != PageSize*2 { return fmt.Errorf("expected %d hex chars, but got %d", PageSize*2, len(dat)) } @@ -61,7 +61,7 @@ func (p *Page) UnmarshalText(dat []byte) error { } type CachedPage struct { - Data *Page + Data Page // intermediate nodes only Cache [PageSize / 32][32]byte // bit set to 1 if the intermediate node is valid diff --git a/cannon/mipsevm/memory/page_test.go b/cannon/mipsevm/memory/page_test.go index e7a8167a9df49..81a0a612bd580 100644 --- a/cannon/mipsevm/memory/page_test.go +++ b/cannon/mipsevm/memory/page_test.go @@ -8,7 +8,7 @@ import ( ) func TestCachedPage(t *testing.T) { - p := &CachedPage{Data: new(Page)} + p := &CachedPage{Data: make(Page, PageSize, PageSize)} p.Data[42] = 0xab gindex := ((uint64(1) << PageAddrSize) | 42) >> 5 diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index cc3bdf2c52bf5..ea52c50657bf5 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -24,11 +24,41 @@ func (m *InstrumentedState) handleSyscall() error { v0 := Word(0) v1 := Word(0) + // func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) { + // v1 = Word(0) + // newHeap = heap + + // sz := a1 + // if sz&memory.PageAddrMask != 0 { // adjust size to align with page size + // sz += memory.PageSize - (sz & memory.PageAddrMask) + // } + // if a0 == 0 { + // v0 = heap + // v0bytes := make([]byte, 8) + // binary.BigEndian.PutUint64(v0bytes, uint64(v0)) + // fmt.Printf("mmap heap % X size 0x%x\n", v0bytes, sz) + // newHeap += sz + // // Fail if new heap exceeds memory limit, newHeap overflows around to low memory, or sz overflows + // if newHeap > program.HEAP_END || newHeap < heap || sz < a1 { + // v0 = SysErrorSignal + // v1 = MipsEINVAL + // return v0, v1, heap + // } + // } else { + // v0 = a0 + // v0bytes := make([]byte, 8) + // binary.BigEndian.PutUint64(v0bytes, uint64(v0)) + // fmt.Printf("mmap hint % X size 0x%x\n", v0bytes, sz) + // } + + // return v0, v1, newHeap + // } //fmt.Printf("syscall: %d\n", syscallNum) switch syscallNum { case arch.SysMmap: var newHeap Word v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) + m.state.Memory.Mmap(a0, a1, newHeap) m.state.Heap = newHeap case arch.SysBrk: v0 = program.PROGRAM_BREAK From 959804267e64f65fd5d0e6d371440e2194261178 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 18 Feb 2025 14:45:40 -0500 Subject: [PATCH 04/10] remove clutter --- cannon/cmd/run.go | 30 +++++------ cannon/mipsevm/memory/binary_tree.go | 2 +- cannon/mipsevm/memory/memory.go | 80 +++++++++------------------- 3 files changed, 42 insertions(+), 70 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index cc1b658eaa495..69dc312055a15 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -441,21 +441,21 @@ func Run(ctx *cli.Context) error { // "gap", r[3], // ) // } - totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion - totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion - l.Info("Read Section Percentages", - "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), - "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), - "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), - "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), - ) - - l.Info("Write Section Percentages", - "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), - "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), - "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), - "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), - ) + // totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion + // totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion + // l.Info("Read Section Percentages", + // "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), + // "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), + // "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), + // "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), + // ) + + // l.Info("Write Section Percentages", + // "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), + // "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), + // "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), + // "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), + // ) insn := mipsexec.LoadSubWord(state.GetMemory(), pc, 4, false, new(mipsexec.NoopMemoryTracker)) l.Info("processing", diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index f742868e9aebc..ccae3945d81cd 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -75,7 +75,7 @@ func NewBinaryTreeMemory() *Memory { merkleIndex: index, pageTable: pages, lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - ProgramRegion: make([]*CachedPage, 0, 1<<31), + ProgramRegion: make([]byte, 0, 1<<31), } } diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index a52aeadef7c08..0ad3c0b0fb97b 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -54,17 +54,17 @@ type Memory struct { lastPageKeys [2]Word lastPage [2]*CachedPage - ProgramRegion []*CachedPage + ProgramRegion []byte - RprogramRegion uint64 - RmallocRegion uint64 - RheapRegion uint64 - RstackRegion uint64 + // RprogramRegion uint64 + // RmallocRegion uint64 + // RheapRegion uint64 + // RstackRegion uint64 - WprogramRegion uint64 - WmallocRegion uint64 - WheapRegion uint64 - WstackRegion uint64 + // WprogramRegion uint64 + // WmallocRegion uint64 + // WheapRegion uint64 + // WstackRegion uint64 } type PageIndex interface { @@ -83,9 +83,10 @@ func (m *Memory) Rejig() { slices.Sort(indexes) for _, pageIdx := range indexes { if pageIdx<>arch.PageAddrSize { - // fmt.Printf("PageLookup under heapstart: %x\n", pageIndex) - return m.ProgramRegion[pageIndex], true - } // hit caches if pageIndex == m.lastPageKeys[0] { return m.lastPage[0], true @@ -184,13 +181,6 @@ func (m *Memory) Mmap(addr, v1, newheap Word) { func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error { for { - if !arch.IsMips32 { - seg := addr >> (48 - 2) - if seg == 1 { - fmt.Printf("SetMemoryRange addr: %x seg: %d\n", addr, seg) - } - } - pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask readLen := PageSize - pageAddr @@ -219,17 +209,7 @@ func (m *Memory) SetWord(addr Word, v Word) { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - if addr < arch.ProgramHeapStart { - m.WprogramRegion++ - } else if addr < arch.HeapStart { - m.WmallocRegion++ - } else if addr < arch.HighMemoryStart { - m.WheapRegion++ - } else if addr < arch.Limit { - m.WstackRegion++ - } else { - panic(fmt.Errorf("invalid memory write: %x", addr)) - } + pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask p, ok := m.PageLookup(pageIndex) @@ -255,22 +235,13 @@ func (m *Memory) GetWord(addr Word) Word { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } + if addr < arch.ProgramHeapStart { - m.RprogramRegion++ - } else if addr < arch.HeapStart { - m.RmallocRegion++ - } else if addr < arch.HighMemoryStart { - m.RheapRegion++ - } else if addr < arch.Limit { - m.RstackRegion++ - } else { - panic(fmt.Errorf("invalid memory write: %x", addr)) + // fmt.Printf("PageLookup under heapstart: %x\n", pageIndex) + return arch.ByteOrderWord.Word(m.ProgramRegion[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes]) } pageIndex := addr >> PageAddrSize - seg := addr >> (48 - 2) - if seg == 2 { - fmt.Printf("GetWord addr: %x seg: %d\n", addr, seg) - } + p, ok := m.PageLookup(pageIndex) if !ok { return 0 @@ -280,18 +251,19 @@ func (m *Memory) GetWord(addr Word) Word { } func (m *Memory) AllocPage(pageIndex Word) *CachedPage { - // fmt.Printf("AllocPage pageIndex: %x\n", pageIndex) - // fmt.Printf("ProgramRegion: %d\n", len(m.ProgramRegion)) + p := new(CachedPage) if pageIndex < arch.ProgramHeapStart>>arch.PageAddrSize { proglen := len(m.ProgramRegion) - if pageIndex >= Word(proglen) { - m.ProgramRegion = m.ProgramRegion[:pageIndex+1] + if pageIndex*PageSize >= Word(proglen) { + m.ProgramRegion = m.ProgramRegion[:(pageIndex+1)*PageSize] } - m.ProgramRegion[pageIndex] = p + p.Data = m.ProgramRegion[pageIndex*PageSize : (pageIndex+1)*PageSize : (pageIndex+1)*PageSize] + } else { + p.Data = make(Page, PageSize, PageSize) } - p.Data = make(Page, PageSize) + // p.Data = make(Page, PageSize) m.pageTable[pageIndex] = p m.merkleIndex.AddPage(pageIndex) return p @@ -361,7 +333,7 @@ func (m *Memory) Copy() *Memory { pageTable: pages, lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages lastPage: [2]*CachedPage{nil, nil}, - ProgramRegion: make([]*CachedPage, len(m.ProgramRegion), 1<<31), + ProgramRegion: make([]byte, len(m.ProgramRegion), 1<<31), } for k, page := range m.pageTable { From 34175c2f0244ab288cb70a977fa2f71e08ece4c9 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 18 Feb 2025 16:25:59 -0500 Subject: [PATCH 05/10] fix --- cannon/cmd/run.go | 30 ++++++++++++------------ cannon/mipsevm/memory/memory.go | 41 +++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 69dc312055a15..cc1b658eaa495 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -441,21 +441,21 @@ func Run(ctx *cli.Context) error { // "gap", r[3], // ) // } - // totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion - // totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion - // l.Info("Read Section Percentages", - // "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), - // "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), - // "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), - // "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), - // ) - - // l.Info("Write Section Percentages", - // "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), - // "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), - // "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), - // "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), - // ) + totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion + totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion + l.Info("Read Section Percentages", + "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), + "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), + "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), + "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), + ) + + l.Info("Write Section Percentages", + "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), + "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), + "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), + "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), + ) insn := mipsexec.LoadSubWord(state.GetMemory(), pc, 4, false, new(mipsexec.NoopMemoryTracker)) l.Info("processing", diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 0ad3c0b0fb97b..206f461a01da4 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -56,15 +56,15 @@ type Memory struct { ProgramRegion []byte - // RprogramRegion uint64 - // RmallocRegion uint64 - // RheapRegion uint64 - // RstackRegion uint64 - - // WprogramRegion uint64 - // WmallocRegion uint64 - // WheapRegion uint64 - // WstackRegion uint64 + RprogramRegion uint64 + RmallocRegion uint64 + RheapRegion uint64 + RstackRegion uint64 + + WprogramRegion uint64 + WmallocRegion uint64 + WheapRegion uint64 + WstackRegion uint64 } type PageIndex interface { @@ -209,7 +209,17 @@ func (m *Memory) SetWord(addr Word, v Word) { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - + if addr < arch.ProgramHeapStart { + m.WprogramRegion++ + } else if addr < arch.HeapStart { + m.WmallocRegion++ + } else if addr < arch.HighMemoryStart { + m.WheapRegion++ + } else if addr < arch.Limit { + m.WstackRegion++ + } else { + panic(fmt.Errorf("invalid memory write: %x", addr)) + } pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask p, ok := m.PageLookup(pageIndex) @@ -236,6 +246,17 @@ func (m *Memory) GetWord(addr Word) Word { panic(fmt.Errorf("unaligned memory access: %x", addr)) } + if addr < arch.ProgramHeapStart { + m.RprogramRegion++ + } else if addr < arch.HeapStart { + m.RmallocRegion++ + } else if addr < arch.HighMemoryStart { + m.RheapRegion++ + } else if addr < arch.Limit { + m.RstackRegion++ + } else { + panic(fmt.Errorf("invalid memory read: %x", addr)) + } if addr < arch.ProgramHeapStart { // fmt.Printf("PageLookup under heapstart: %x\n", pageIndex) return arch.ByteOrderWord.Word(m.ProgramRegion[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes]) From b5957081301ec4d5469f0f6ddede9d953ae66e20 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 18 Feb 2025 17:10:29 -0500 Subject: [PATCH 06/10] gomallocregion --- cannon/cmd/run.go | 30 ++++---- cannon/mipsevm/memory/binary_tree.go | 9 ++- cannon/mipsevm/memory/memory.go | 106 ++++++++++++++++----------- cannon/mipsevm/multithreaded/mips.go | 30 -------- 4 files changed, 85 insertions(+), 90 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index cc1b658eaa495..69dc312055a15 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -441,21 +441,21 @@ func Run(ctx *cli.Context) error { // "gap", r[3], // ) // } - totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion - totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion - l.Info("Read Section Percentages", - "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), - "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), - "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), - "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), - ) - - l.Info("Write Section Percentages", - "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), - "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), - "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), - "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), - ) + // totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion + // totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion + // l.Info("Read Section Percentages", + // "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), + // "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), + // "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), + // "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), + // ) + + // l.Info("Write Section Percentages", + // "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), + // "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), + // "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), + // "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), + // ) insn := mipsexec.LoadSubWord(state.GetMemory(), pc, 4, false, new(mipsexec.NoopMemoryTracker)) l.Info("processing", diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index ccae3945d81cd..751f3a36d6cdc 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -72,10 +72,11 @@ func NewBinaryTreeMemory() *Memory { index := NewBinaryTreeIndex() index.setPageBacking(pages) return &Memory{ - merkleIndex: index, - pageTable: pages, - lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - ProgramRegion: make([]byte, 0, 1<<31), + merkleIndex: index, + pageTable: pages, + lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + ProgramRegion: make([]byte, 0, 1<<31), + GoMallocRegion: make([]byte, 0, 1<<31), } } diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 206f461a01da4..04cb738d7f9f2 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -54,17 +54,18 @@ type Memory struct { lastPageKeys [2]Word lastPage [2]*CachedPage - ProgramRegion []byte - - RprogramRegion uint64 - RmallocRegion uint64 - RheapRegion uint64 - RstackRegion uint64 - - WprogramRegion uint64 - WmallocRegion uint64 - WheapRegion uint64 - WstackRegion uint64 + ProgramRegion []byte + GoMallocRegion []byte + + // RprogramRegion uint64 + // RmallocRegion uint64 + // RheapRegion uint64 + // RstackRegion uint64 + + // WprogramRegion uint64 + // WmallocRegion uint64 + // WheapRegion uint64 + // WstackRegion uint64 } type PageIndex interface { @@ -82,11 +83,16 @@ func (m *Memory) Rejig() { indexes := maps.Keys(m.pageTable) slices.Sort(indexes) for _, pageIdx := range indexes { - if pageIdx<> PageAddrSize pageAddr := addr & PageAddrMask p, ok := m.PageLookup(pageIndex) @@ -246,20 +260,22 @@ func (m *Memory) GetWord(addr Word) Word { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - if addr < arch.ProgramHeapStart { - m.RprogramRegion++ - } else if addr < arch.HeapStart { - m.RmallocRegion++ - } else if addr < arch.HighMemoryStart { - m.RheapRegion++ - } else if addr < arch.Limit { - m.RstackRegion++ - } else { - panic(fmt.Errorf("invalid memory read: %x", addr)) - } + // if addr < arch.ProgramHeapStart { + // m.RprogramRegion++ + // } else if addr < arch.HeapStart { + // m.RmallocRegion++ + // } else if addr < arch.HighMemoryStart { + // m.RheapRegion++ + // } else if addr < arch.Limit { + // m.RstackRegion++ + // } else { + // panic(fmt.Errorf("invalid memory read: %x", addr)) + // } if addr < arch.ProgramHeapStart { // fmt.Printf("PageLookup under heapstart: %x\n", pageIndex) return arch.ByteOrderWord.Word(m.ProgramRegion[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes]) + } else if addr < arch.HeapStart { + return arch.ByteOrderWord.Word(m.GoMallocRegion[addr-arch.ProgramHeapStart : addr-arch.ProgramHeapStart+arch.WordSizeBytes : addr-arch.ProgramHeapStart+arch.WordSizeBytes]) } pageIndex := addr >> PageAddrSize @@ -280,6 +296,13 @@ func (m *Memory) AllocPage(pageIndex Word) *CachedPage { m.ProgramRegion = m.ProgramRegion[:(pageIndex+1)*PageSize] } p.Data = m.ProgramRegion[pageIndex*PageSize : (pageIndex+1)*PageSize : (pageIndex+1)*PageSize] + } else if pageIndex < arch.HeapStart>>arch.PageAddrSize { + proglen := len(m.GoMallocRegion) + indexAdjusted := pageIndex - arch.ProgramHeapStart>>arch.PageAddrSize + if indexAdjusted*PageSize >= Word(proglen) { + m.GoMallocRegion = m.GoMallocRegion[:(indexAdjusted+1)*PageSize] + } + p.Data = m.GoMallocRegion[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize : (indexAdjusted+1)*PageSize] } else { p.Data = make(Page, PageSize, PageSize) } @@ -350,11 +373,12 @@ func (m *Memory) Copy() *Memory { table := m.merkleIndex.New() table.setPageBacking(pages) out := &Memory{ - merkleIndex: table, - pageTable: pages, - lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - lastPage: [2]*CachedPage{nil, nil}, - ProgramRegion: make([]byte, len(m.ProgramRegion), 1<<31), + merkleIndex: table, + pageTable: pages, + lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + lastPage: [2]*CachedPage{nil, nil}, + ProgramRegion: make([]byte, len(m.ProgramRegion), 1<<31), + GoMallocRegion: make([]byte, len(m.GoMallocRegion), 1<<31), } for k, page := range m.pageTable { diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index ea52c50657bf5..b12fbb0332cae 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -24,36 +24,6 @@ func (m *InstrumentedState) handleSyscall() error { v0 := Word(0) v1 := Word(0) - // func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) { - // v1 = Word(0) - // newHeap = heap - - // sz := a1 - // if sz&memory.PageAddrMask != 0 { // adjust size to align with page size - // sz += memory.PageSize - (sz & memory.PageAddrMask) - // } - // if a0 == 0 { - // v0 = heap - // v0bytes := make([]byte, 8) - // binary.BigEndian.PutUint64(v0bytes, uint64(v0)) - // fmt.Printf("mmap heap % X size 0x%x\n", v0bytes, sz) - // newHeap += sz - // // Fail if new heap exceeds memory limit, newHeap overflows around to low memory, or sz overflows - // if newHeap > program.HEAP_END || newHeap < heap || sz < a1 { - // v0 = SysErrorSignal - // v1 = MipsEINVAL - // return v0, v1, heap - // } - // } else { - // v0 = a0 - // v0bytes := make([]byte, 8) - // binary.BigEndian.PutUint64(v0bytes, uint64(v0)) - // fmt.Printf("mmap hint % X size 0x%x\n", v0bytes, sz) - // } - - // return v0, v1, newHeap - // } - //fmt.Printf("syscall: %d\n", syscallNum) switch syscallNum { case arch.SysMmap: var newHeap Word From b7b78989d9fda255559a7c2837d4a7c2ec3e65fe Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Thu, 20 Feb 2025 15:29:43 -0500 Subject: [PATCH 07/10] more readable mapped regions --- cannon/cmd/run.go | 2 +- cannon/mipsevm/memory/binary_tree.go | 22 +++- cannon/mipsevm/memory/memory.go | 115 +++++++++++-------- cannon/mipsevm/multithreaded/instrumented.go | 11 ++ cannon/mipsevm/multithreaded/mips.go | 15 ++- 5 files changed, 106 insertions(+), 59 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 69dc312055a15..43ad2a47af0a5 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -414,7 +414,7 @@ func Run(ctx *cli.Context) error { "gap", r[3], ) } - state.GetMemory().Rejig() + state.GetMemory().InitMappedRegions() start := time.Now() startStep := state.GetStep() diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index 751f3a36d6cdc..5a201e409da9c 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -4,6 +4,7 @@ import ( "math/bits" "sync" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" "github.com/ethereum/go-ethereum/crypto" ) @@ -71,12 +72,23 @@ func NewBinaryTreeMemory() *Memory { pages := make(map[Word]*CachedPage) index := NewBinaryTreeIndex() index.setPageBacking(pages) + + indexedRegions := make([]MappedMemoryRegion, 2) + indexedRegions[0] = MappedMemoryRegion{ + start_addr: 0, + end_addr: arch.ProgramHeapStart, + data: make([]byte, 1<<31), + } + indexedRegions[1] = MappedMemoryRegion{ + start_addr: arch.ProgramHeapStart, + end_addr: arch.HeapStart, + data: make([]byte, 1<<31), + } return &Memory{ - merkleIndex: index, - pageTable: pages, - lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - ProgramRegion: make([]byte, 0, 1<<31), - GoMallocRegion: make([]byte, 0, 1<<31), + merkleIndex: index, + pageTable: pages, + lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + mappedRegions: indexedRegions, } } diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 04cb738d7f9f2..26fda9917ee87 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -43,6 +43,27 @@ var zeroHashes = func() [256][32]byte { return out }() +type MappedMemoryRegion struct { + start_addr Word + end_addr Word + data []byte +} + +func (m *MappedMemoryRegion) AddrInRegion(addr Word) bool { + return addr >= m.start_addr && addr < m.end_addr +} + +func (m *MappedMemoryRegion) PageIndexInRegion(pageIndex Word) bool { + return pageIndex >= m.start_addr>>PageAddrSize && pageIndex < m.end_addr>>PageAddrSize +} + +func (m *MappedMemoryRegion) AccessWordBytes(addr Word) ([]byte, bool) { + if m.AddrInRegion(addr) { + return m.data[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes], true + } + return nil, false +} + type Memory struct { merkleIndex PageIndex // Note: since we don't de-alloc Pages, we don't do ref-counting. @@ -54,9 +75,7 @@ type Memory struct { lastPageKeys [2]Word lastPage [2]*CachedPage - ProgramRegion []byte - GoMallocRegion []byte - + mappedRegions []MappedMemoryRegion // RprogramRegion uint64 // RmallocRegion uint64 // RheapRegion uint64 @@ -79,20 +98,21 @@ type PageIndex interface { setPageBacking(pages map[Word]*CachedPage) } -func (m *Memory) Rejig() { +func (m *Memory) InitMappedRegions() { indexes := maps.Keys(m.pageTable) slices.Sort(indexes) for _, pageIdx := range indexes { - if pageIdx<>PageAddrSize + if indexAdjusted*PageSize >= Word(currLen) { + region.data = region.data[:(pageIdx+1)*PageSize] + } + copy(region.data[indexAdjusted*PageSize:(indexAdjusted+1)*PageSize], m.pageTable[pageIdx].Data) + m.pageTable[pageIdx].Data = region.data[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize] + break + } } } } @@ -182,15 +202,13 @@ func (m *Memory) PageLookup(pageIndex Word) (*CachedPage, bool) { } func (m *Memory) Mmap(addr, size, newheap Word) { - if addr < arch.ProgramHeapStart { - fmt.Printf("Mmap Pre Heap: %x size: %x newheap: %x\n", addr, size, newheap) - } else if addr < arch.HeapStart { - fmt.Printf("Mmap Go Heap: %x size: %x newheap: %x\n", addr, size, newheap) - m.GoMallocRegion = m.GoMallocRegion[:addr-arch.ProgramHeapStart+size] - } else { + for _, region := range m.mappedRegions { + if region.AddrInRegion(addr) { + region.data = region.data[:addr-region.start_addr+size] + break + } } fmt.Printf("Mmap addr: %x size: %x newheap: %x\n", addr, size, newheap) - } func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error { @@ -271,12 +289,13 @@ func (m *Memory) GetWord(addr Word) Word { // } else { // panic(fmt.Errorf("invalid memory read: %x", addr)) // } - if addr < arch.ProgramHeapStart { - // fmt.Printf("PageLookup under heapstart: %x\n", pageIndex) - return arch.ByteOrderWord.Word(m.ProgramRegion[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes]) - } else if addr < arch.HeapStart { - return arch.ByteOrderWord.Word(m.GoMallocRegion[addr-arch.ProgramHeapStart : addr-arch.ProgramHeapStart+arch.WordSizeBytes : addr-arch.ProgramHeapStart+arch.WordSizeBytes]) + for _, region := range m.mappedRegions { + if ok := region.AddrInRegion(addr); ok { + offset := addr - region.start_addr + return arch.ByteOrderWord.Word(region.data[offset : offset+arch.WordSizeBytes : offset+arch.WordSizeBytes]) + } } + pageIndex := addr >> PageAddrSize p, ok := m.PageLookup(pageIndex) @@ -288,26 +307,21 @@ func (m *Memory) GetWord(addr Word) Word { } func (m *Memory) AllocPage(pageIndex Word) *CachedPage { - p := new(CachedPage) - if pageIndex < arch.ProgramHeapStart>>arch.PageAddrSize { - proglen := len(m.ProgramRegion) - if pageIndex*PageSize >= Word(proglen) { - m.ProgramRegion = m.ProgramRegion[:(pageIndex+1)*PageSize] - } - p.Data = m.ProgramRegion[pageIndex*PageSize : (pageIndex+1)*PageSize : (pageIndex+1)*PageSize] - } else if pageIndex < arch.HeapStart>>arch.PageAddrSize { - proglen := len(m.GoMallocRegion) - indexAdjusted := pageIndex - arch.ProgramHeapStart>>arch.PageAddrSize - if indexAdjusted*PageSize >= Word(proglen) { - m.GoMallocRegion = m.GoMallocRegion[:(indexAdjusted+1)*PageSize] + for _, region := range m.mappedRegions { + if region.PageIndexInRegion(pageIndex) { + currLen := len(region.data) + indexAdjusted := pageIndex - region.start_addr>>PageAddrSize + if indexAdjusted*PageSize >= Word(currLen) { + region.data = region.data[:(pageIndex+1)*PageSize] + } + p.Data = region.data[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize : (indexAdjusted+1)*PageSize] + break } - p.Data = m.GoMallocRegion[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize : (indexAdjusted+1)*PageSize] - } else { - p.Data = make(Page, PageSize, PageSize) } - - // p.Data = make(Page, PageSize) + if p.Data == nil { + p.Data = make(Page, PageSize) + } m.pageTable[pageIndex] = p m.merkleIndex.AddPage(pageIndex) return p @@ -373,16 +387,17 @@ func (m *Memory) Copy() *Memory { table := m.merkleIndex.New() table.setPageBacking(pages) out := &Memory{ - merkleIndex: table, - pageTable: pages, - lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - lastPage: [2]*CachedPage{nil, nil}, - ProgramRegion: make([]byte, len(m.ProgramRegion), 1<<31), - GoMallocRegion: make([]byte, len(m.GoMallocRegion), 1<<31), + merkleIndex: table, + pageTable: pages, + lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages + lastPage: [2]*CachedPage{nil, nil}, + // ProgramRegion: make([]byte, len(m.ProgramRegion), 1<<31), + // GoMallocRegion: make([]byte, len(m.GoMallocRegion), 1<<31), + } for k, page := range m.pageTable { - data := make(Page, PageSize, PageSize) + data := make(Page, PageSize) // *data = *page.Data copy(data, page.Data) out.AllocPage(k).Data = data diff --git a/cannon/mipsevm/multithreaded/instrumented.go b/cannon/mipsevm/multithreaded/instrumented.go index 5295d74811616..812ff61a6110f 100644 --- a/cannon/mipsevm/multithreaded/instrumented.go +++ b/cannon/mipsevm/multithreaded/instrumented.go @@ -11,6 +11,9 @@ import ( "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" ) +type InstructionDetails struct { + insn, opcode, fun uint32 +} type InstrumentedState struct { state *State @@ -24,11 +27,18 @@ type InstrumentedState struct { preimageOracle *exec.TrackingPreimageOracleReader meta mipsevm.Metadata + + // cached_decode []InstructionDetails } var _ mipsevm.FPVM = (*InstrumentedState)(nil) func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta mipsevm.Metadata) *InstrumentedState { + // cached_decode := make([]InstructionDetails, 0) + // for pc := Word(0); pc < Word(len(state.Memory.ProgramRegion)); pc += 4 { + // insn, opcode, fun := exec.GetInstructionDetails(pc, state.Memory) + // cached_decode = append(cached_decode, InstructionDetails{insn, opcode, fun}) + // } return &InstrumentedState{ state: state, log: log, @@ -39,6 +49,7 @@ func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdEr statsTracker: NoopStatsTracker(), preimageOracle: exec.NewTrackingPreimageOracleReader(po), meta: meta, + // cached_decode: cached_decode, } } diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index b12fbb0332cae..81cb611cb7c72 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -254,10 +254,19 @@ func (m *InstrumentedState) doMipsStep() error { return nil } m.state.StepsSinceLastContextSwitch += 1 - - //instruction fetch + // var insn, opcode, fun uint32 + // if deet, ok := m.cached_decode[pc]; ok { + // //instruction fetch + // insn, opcode, fun = deet.insn, deet.opcode, deet.fun + // } else { + // //instruction fetch + // insn, opcode, fun = exec.GetInstructionDetails(m.state.GetPC(), m.state.Memory) + // m.cached_decode[pc] = InstructionDetails{insn, opcode, fun} + // } + + // ca := m.cached_decode[pc/4] + // insn, opcode, fun := ca.insn, ca.opcode, ca.fun insn, opcode, fun := exec.GetInstructionDetails(m.state.GetPC(), m.state.Memory) - // Handle syscall separately // syscall (can read and write) if opcode == 0 && fun == 0xC { From 892f91a2c222f12af51a62e4f9173934d0a73cd6 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Mon, 3 Mar 2025 15:25:01 -0500 Subject: [PATCH 08/10] clean --- cannon/mipsevm/memory/binary_tree.go | 4 +- cannon/mipsevm/memory/memory.go | 57 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index 1608848045e19..2278ae2e49676 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -21,12 +21,12 @@ func NewBinaryTreeMemory() *Memory { indexedRegions := make([]MappedMemoryRegion, 2) indexedRegions[0] = MappedMemoryRegion{ start_addr: 0, - end_addr: arch.ProgramHeapStart, + end_addr: min(arch.ProgramHeapStart, 1<<31), data: make([]byte, 1<<31), } indexedRegions[1] = MappedMemoryRegion{ start_addr: arch.ProgramHeapStart, - end_addr: arch.HeapStart, + end_addr: min(arch.HeapStart-arch.ProgramHeapStart, arch.ProgramHeapStart+(1<<31)), data: make([]byte, 1<<31), } return &Memory{ diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 39b8cf0af6d3f..919b7d6e5bc9c 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -27,6 +27,27 @@ const ( type Word = arch.Word +type MappedMemoryRegion struct { + start_addr Word + end_addr Word + data []byte +} + +func (m *MappedMemoryRegion) AddrInRegion(addr Word) bool { + return addr >= m.start_addr && addr < m.end_addr +} + +func (m *MappedMemoryRegion) PageIndexInRegion(pageIndex Word) bool { + return pageIndex >= m.start_addr>>PageAddrSize && pageIndex < m.end_addr>>PageAddrSize +} + +func (m *MappedMemoryRegion) AccessWordBytes(addr Word) ([]byte, bool) { + if m.AddrInRegion(addr) { + return m.data[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes], true + } + return nil, false +} + type Memory struct { merkleIndex PageIndex // Note: since we don't de-alloc Pages, we don't do ref-counting. @@ -160,12 +181,12 @@ func (m *Memory) PageLookup(pageIndex Word) (*CachedPage, bool) { } func (m *Memory) Mmap(addr, size, newheap Word) { - for _, region := range m.mappedRegions { - if region.AddrInRegion(addr) { - region.data = region.data[:addr-region.start_addr+size] - break - } - } + // for _, region := range m.mappedRegions { + // if region.AddrInRegion(addr) { + // region.data = region.data[:addr-region.start_addr+size] + // break + // } + // } fmt.Printf("Mmap addr: %x size: %x newheap: %x\n", addr, size, newheap) } @@ -199,17 +220,7 @@ func (m *Memory) SetWord(addr Word, v Word) { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - // if addr < arch.ProgramHeapStart { - // m.WprogramRegion++ - // } else if addr < arch.HeapStart { - // m.WmallocRegion++ - // } else if addr < arch.HighMemoryStart { - // m.WheapRegion++ - // } else if addr < arch.Limit { - // m.WstackRegion++ - // } else { - // panic(fmt.Errorf("invalid memory write: %x", addr)) - // } + pageIndex := addr >> PageAddrSize pageAddr := addr & PageAddrMask p, ok := m.PageLookup(pageIndex) @@ -235,18 +246,6 @@ func (m *Memory) GetWord(addr Word) Word { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - - // if addr < arch.ProgramHeapStart { - // m.RprogramRegion++ - // } else if addr < arch.HeapStart { - // m.RmallocRegion++ - // } else if addr < arch.HighMemoryStart { - // m.RheapRegion++ - // } else if addr < arch.Limit { - // m.RstackRegion++ - // } else { - // panic(fmt.Errorf("invalid memory read: %x", addr)) - // } for _, region := range m.mappedRegions { if ok := region.AddrInRegion(addr); ok { offset := addr - region.start_addr From ef422bb5b73c357e0eef4e6a5e583635032a14dd Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Mon, 3 Mar 2025 15:46:37 -0500 Subject: [PATCH 09/10] clean up some --- cannon/cmd/run.go | 41 +------------------- cannon/mipsevm/exec/mips_syscalls.go | 9 +---- cannon/mipsevm/memory/binary_tree.go | 6 +-- cannon/mipsevm/memory/memory.go | 41 +++++--------------- cannon/mipsevm/multithreaded/instrumented.go | 14 +++---- cannon/mipsevm/multithreaded/mips.go | 19 +++------ 6 files changed, 28 insertions(+), 102 deletions(-) diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 9955d5af25b93..63bb1c51c6ab3 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -388,7 +388,6 @@ func Run(ctx *cli.Context) error { } state, err := versions.LoadStateFromFile(ctx.Path(RunInputFlag.Name)) - if err != nil { return fmt.Errorf("failed to load state: %w", err) } @@ -416,18 +415,7 @@ func Run(ctx *cli.Context) error { if po.cmd != nil { stepFn = Guard(po.cmd.ProcessState, stepFn) } - fmt.Printf("Initial memory Range") - ranges := state.GetMemory().GetAllocatedRanges() - for i, r := range ranges { - l.Info("memory range", - "num", i, - "start", fmt.Sprintf("%#x", r[0]), - "end", fmt.Sprintf("%#x", r[1]), - "size", r[2], - "gap", r[3], - ) - } - state.GetMemory().InitMappedRegions() + start := time.Now() startStep := state.GetStep() @@ -443,33 +431,6 @@ func Run(ctx *cli.Context) error { if infoAt(state) { delta := time.Since(start) pc := state.GetPC() - - // ranges := state.GetMemory().GetAllocatedRanges() - // for i, r := range ranges { - // l.Info("memory range", - // "num", i, - // "start", fmt.Sprintf("%#x", r[0]), - // "end", fmt.Sprintf("%#x", r[1]), - // "size", r[2], - // "gap", r[3], - // ) - // } - // totalRead := state.GetMemory().RprogramRegion + state.GetMemory().RmallocRegion + state.GetMemory().RheapRegion + state.GetMemory().RstackRegion - // totalWrite := state.GetMemory().WprogramRegion + state.GetMemory().WmallocRegion + state.GetMemory().WheapRegion + state.GetMemory().WstackRegion - // l.Info("Read Section Percentages", - // "RprogramRegion", float64(state.GetMemory().RprogramRegion)/float64(totalRead), - // "RmallocRegion", float64(state.GetMemory().RmallocRegion)/float64(totalRead), - // "RheapRegion", float64(state.GetMemory().RheapRegion)/float64(totalRead), - // "RstackRegion", float64(state.GetMemory().RstackRegion)/float64(totalRead), - // ) - - // l.Info("Write Section Percentages", - // "WprogramRegion", float64(state.GetMemory().WprogramRegion)/float64(totalWrite), - // "WmallocRegion", float64(state.GetMemory().WmallocRegion)/float64(totalWrite), - // "WheapRegion", float64(state.GetMemory().WheapRegion)/float64(totalWrite), - // "WstackRegion", float64(state.GetMemory().WstackRegion)/float64(totalWrite), - // ) - insn := mipsexec.LoadSubWord(state.GetMemory(), pc, 4, false, new(mipsexec.NoopMemoryTracker)) l.Info("processing", "step", step, diff --git a/cannon/mipsevm/exec/mips_syscalls.go b/cannon/mipsevm/exec/mips_syscalls.go index 701cc2a55b1c9..ca0801d9d659f 100644 --- a/cannon/mipsevm/exec/mips_syscalls.go +++ b/cannon/mipsevm/exec/mips_syscalls.go @@ -2,7 +2,6 @@ package exec import ( "encoding/binary" - "fmt" "io" "github.com/ethereum/go-ethereum/common" @@ -117,9 +116,7 @@ func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) { } if a0 == 0 { v0 = heap - v0bytes := make([]byte, 8) - binary.BigEndian.PutUint64(v0bytes, uint64(v0)) - fmt.Printf("mmap heap %x size 0x%x\n", v0bytes, sz) + //fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz) newHeap += sz // Fail if new heap exceeds memory limit, newHeap overflows around to low memory, or sz overflows if newHeap > program.HEAP_END || newHeap < heap || sz < a1 { @@ -129,9 +126,7 @@ func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) { } } else { v0 = a0 - v0bytes := make([]byte, 8) - binary.BigEndian.PutUint64(v0bytes, uint64(v0)) - fmt.Printf("mmap hint %x size 0x%x\n", v0bytes, sz) + //fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz) } return v0, v1, newHeap diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index 2278ae2e49676..dab619b0230f2 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -22,18 +22,18 @@ func NewBinaryTreeMemory() *Memory { indexedRegions[0] = MappedMemoryRegion{ start_addr: 0, end_addr: min(arch.ProgramHeapStart, 1<<31), - data: make([]byte, 1<<31), + Data: make([]byte, 1<<31), } indexedRegions[1] = MappedMemoryRegion{ start_addr: arch.ProgramHeapStart, end_addr: min(arch.HeapStart-arch.ProgramHeapStart, arch.ProgramHeapStart+(1<<31)), - data: make([]byte, 1<<31), + Data: make([]byte, 1<<31), } return &Memory{ merkleIndex: index, pageTable: pages, lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages - mappedRegions: indexedRegions, + MappedRegions: indexedRegions, } } diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 919b7d6e5bc9c..47c5330609b16 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -30,7 +30,7 @@ type Word = arch.Word type MappedMemoryRegion struct { start_addr Word end_addr Word - data []byte + Data []byte } func (m *MappedMemoryRegion) AddrInRegion(addr Word) bool { @@ -43,7 +43,7 @@ func (m *MappedMemoryRegion) PageIndexInRegion(pageIndex Word) bool { func (m *MappedMemoryRegion) AccessWordBytes(addr Word) ([]byte, bool) { if m.AddrInRegion(addr) { - return m.data[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes], true + return m.Data[addr : addr+arch.WordSizeBytes : addr+arch.WordSizeBytes], true } return nil, false } @@ -60,7 +60,7 @@ type Memory struct { lastPageKeys [2]Word lastPage [2]*CachedPage - mappedRegions []MappedMemoryRegion + MappedRegions []MappedMemoryRegion } type PageIndex interface { @@ -77,25 +77,6 @@ func NewMemory() *Memory { return NewBinaryTreeMemory() } -func (m *Memory) InitMappedRegions() { - indexes := maps.Keys(m.pageTable) - slices.Sort(indexes) - for _, pageIdx := range indexes { - for _, region := range m.mappedRegions { - if region.PageIndexInRegion(pageIdx) { - currLen := len(region.data) - indexAdjusted := pageIdx - region.start_addr>>PageAddrSize - if indexAdjusted*PageSize >= Word(currLen) { - region.data = region.data[:(pageIdx+1)*PageSize] - } - copy(region.data[indexAdjusted*PageSize:(indexAdjusted+1)*PageSize], m.pageTable[pageIdx].Data) - m.pageTable[pageIdx].Data = region.data[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize] - break - } - } - } -} - // start end size gap func (m *Memory) GetAllocatedRanges() [][4]Word { var ranges [][4]Word @@ -246,10 +227,10 @@ func (m *Memory) GetWord(addr Word) Word { if addr&arch.ExtMask != 0 { panic(fmt.Errorf("unaligned memory access: %x", addr)) } - for _, region := range m.mappedRegions { + for _, region := range m.MappedRegions { if ok := region.AddrInRegion(addr); ok { offset := addr - region.start_addr - return arch.ByteOrderWord.Word(region.data[offset : offset+arch.WordSizeBytes : offset+arch.WordSizeBytes]) + return arch.ByteOrderWord.Word(region.Data[offset : offset+arch.WordSizeBytes : offset+arch.WordSizeBytes]) } } @@ -265,14 +246,14 @@ func (m *Memory) GetWord(addr Word) Word { func (m *Memory) AllocPage(pageIndex Word) *CachedPage { p := new(CachedPage) - for _, region := range m.mappedRegions { + for _, region := range m.MappedRegions { if region.PageIndexInRegion(pageIndex) { - currLen := len(region.data) + currLen := len(region.Data) indexAdjusted := pageIndex - region.start_addr>>PageAddrSize if indexAdjusted*PageSize >= Word(currLen) { - region.data = region.data[:(pageIndex+1)*PageSize] + region.Data = region.Data[:(pageIndex+1)*PageSize] } - p.Data = region.data[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize : (indexAdjusted+1)*PageSize] + p.Data = region.Data[indexAdjusted*PageSize : (indexAdjusted+1)*PageSize : (indexAdjusted+1)*PageSize] break } } @@ -347,9 +328,6 @@ func (m *Memory) Copy() *Memory { pageTable: pages, lastPageKeys: [2]Word{^Word(0), ^Word(0)}, // default to invalid keys, to not match any pages lastPage: [2]*CachedPage{nil, nil}, - // ProgramRegion: make([]byte, len(m.ProgramRegion), 1<<31), - // GoMallocRegion: make([]byte, len(m.GoMallocRegion), 1<<31), - } for k, page := range m.pageTable { @@ -404,6 +382,7 @@ func (m *Memory) Deserialize(in io.Reader) error { return err } } + return nil } diff --git a/cannon/mipsevm/multithreaded/instrumented.go b/cannon/mipsevm/multithreaded/instrumented.go index 812ff61a6110f..9d4df29b4d2f2 100644 --- a/cannon/mipsevm/multithreaded/instrumented.go +++ b/cannon/mipsevm/multithreaded/instrumented.go @@ -28,17 +28,17 @@ type InstrumentedState struct { preimageOracle *exec.TrackingPreimageOracleReader meta mipsevm.Metadata - // cached_decode []InstructionDetails + cached_decode []InstructionDetails } var _ mipsevm.FPVM = (*InstrumentedState)(nil) func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdErr io.Writer, log log.Logger, meta mipsevm.Metadata) *InstrumentedState { - // cached_decode := make([]InstructionDetails, 0) - // for pc := Word(0); pc < Word(len(state.Memory.ProgramRegion)); pc += 4 { - // insn, opcode, fun := exec.GetInstructionDetails(pc, state.Memory) - // cached_decode = append(cached_decode, InstructionDetails{insn, opcode, fun}) - // } + cached_decode := make([]InstructionDetails, 0) + for pc := Word(0); pc < Word(len(state.Memory.MappedRegions[0].Data)); pc += 4 { + insn, opcode, fun := exec.GetInstructionDetails(pc, state.Memory) + cached_decode = append(cached_decode, InstructionDetails{insn, opcode, fun}) + } return &InstrumentedState{ state: state, log: log, @@ -49,7 +49,7 @@ func NewInstrumentedState(state *State, po mipsevm.PreimageOracle, stdOut, stdEr statsTracker: NoopStatsTracker(), preimageOracle: exec.NewTrackingPreimageOracleReader(po), meta: meta, - // cached_decode: cached_decode, + cached_decode: cached_decode, } } diff --git a/cannon/mipsevm/multithreaded/mips.go b/cannon/mipsevm/multithreaded/mips.go index 81cb611cb7c72..0c9cbf173c110 100644 --- a/cannon/mipsevm/multithreaded/mips.go +++ b/cannon/mipsevm/multithreaded/mips.go @@ -24,11 +24,11 @@ func (m *InstrumentedState) handleSyscall() error { v0 := Word(0) v1 := Word(0) + //fmt.Printf("syscall: %d\n", syscallNum) switch syscallNum { case arch.SysMmap: var newHeap Word v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) - m.state.Memory.Mmap(a0, a1, newHeap) m.state.Heap = newHeap case arch.SysBrk: v0 = program.PROGRAM_BREAK @@ -254,19 +254,10 @@ func (m *InstrumentedState) doMipsStep() error { return nil } m.state.StepsSinceLastContextSwitch += 1 - // var insn, opcode, fun uint32 - // if deet, ok := m.cached_decode[pc]; ok { - // //instruction fetch - // insn, opcode, fun = deet.insn, deet.opcode, deet.fun - // } else { - // //instruction fetch - // insn, opcode, fun = exec.GetInstructionDetails(m.state.GetPC(), m.state.Memory) - // m.cached_decode[pc] = InstructionDetails{insn, opcode, fun} - // } - - // ca := m.cached_decode[pc/4] - // insn, opcode, fun := ca.insn, ca.opcode, ca.fun - insn, opcode, fun := exec.GetInstructionDetails(m.state.GetPC(), m.state.Memory) + + insn_detail := m.cached_decode[m.state.GetPC()/4] + insn, opcode, fun := insn_detail.insn, insn_detail.opcode, insn_detail.fun + // Handle syscall separately // syscall (can read and write) if opcode == 0 && fun == 0xC { From 9ec5408c975091b61de56fa8e440fdde366067fb Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Mon, 3 Mar 2025 16:34:08 -0500 Subject: [PATCH 10/10] fix range --- cannon/mipsevm/memory/binary_tree.go | 2 +- cannon/mipsevm/memory/memory.go | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/cannon/mipsevm/memory/binary_tree.go b/cannon/mipsevm/memory/binary_tree.go index dab619b0230f2..edb48788155fb 100644 --- a/cannon/mipsevm/memory/binary_tree.go +++ b/cannon/mipsevm/memory/binary_tree.go @@ -26,7 +26,7 @@ func NewBinaryTreeMemory() *Memory { } indexedRegions[1] = MappedMemoryRegion{ start_addr: arch.ProgramHeapStart, - end_addr: min(arch.HeapStart-arch.ProgramHeapStart, arch.ProgramHeapStart+(1<<31)), + end_addr: min(arch.HeapStart, arch.ProgramHeapStart+(1<<31)), Data: make([]byte, 1<<31), } return &Memory{ diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index 47c5330609b16..b71a2634acda9 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -161,16 +161,6 @@ func (m *Memory) PageLookup(pageIndex Word) (*CachedPage, bool) { return p, ok } -func (m *Memory) Mmap(addr, size, newheap Word) { - // for _, region := range m.mappedRegions { - // if region.AddrInRegion(addr) { - // region.data = region.data[:addr-region.start_addr+size] - // break - // } - // } - fmt.Printf("Mmap addr: %x size: %x newheap: %x\n", addr, size, newheap) -} - func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error { for { pageIndex := addr >> PageAddrSize