Commit aeaee217 authored by Inphi's avatar Inphi Committed by GitHub

cannon: Avoid page allocation for empty reads (#12747)

* cannon: Avoid page allocation for empty reads

* fix end of chunk overlap
parent a68e3ef4
...@@ -280,18 +280,22 @@ func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error { ...@@ -280,18 +280,22 @@ func (m *Memory) SetMemoryRange(addr Word, r io.Reader) error {
for { for {
pageIndex := addr >> PageAddrSize pageIndex := addr >> PageAddrSize
pageAddr := addr & PageAddrMask pageAddr := addr & PageAddrMask
p, ok := m.pageLookup(pageIndex) readLen := PageSize - pageAddr
if !ok { chunk := make([]byte, readLen)
p = m.AllocPage(pageIndex) n, err := r.Read(chunk)
}
p.InvalidateFull()
n, err := r.Read(p.Data[pageAddr:])
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
return err return err
} }
p, ok := m.pageLookup(pageIndex)
if !ok {
p = m.AllocPage(pageIndex)
}
p.InvalidateFull()
copy(p.Data[pageAddr:], chunk[:n])
addr += Word(n) addr += Word(n)
} }
} }
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -140,6 +141,50 @@ func TestMemory64ReadWrite(t *testing.T) { ...@@ -140,6 +141,50 @@ func TestMemory64ReadWrite(t *testing.T) {
require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end") require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end")
}) })
t.Run("empty range", func(t *testing.T) {
m := NewMemory()
addr := Word(0xAABBCC00)
r := bytes.NewReader(nil)
pre := m.MerkleRoot()
preJSON, err := m.MarshalJSON()
require.NoError(t, err)
var preSerialized bytes.Buffer
require.NoError(t, m.Serialize(&preSerialized))
require.NoError(t, m.SetMemoryRange(addr, r))
v := m.GetWord(0)
require.Equal(t, Word(0), v)
post := m.MerkleRoot()
require.Equal(t, pre, post)
// Assert that there are no extra zero pages in serialization
postJSON, err := m.MarshalJSON()
require.NoError(t, err)
require.Equal(t, preJSON, postJSON)
var postSerialized bytes.Buffer
require.NoError(t, m.Serialize(&postSerialized))
require.Equal(t, preSerialized.Bytes(), postSerialized.Bytes())
})
t.Run("range page overlap", func(t *testing.T) {
m := NewMemory()
data := bytes.Repeat([]byte{0xAA}, PageAddrSize)
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
for i := 0; i < PageAddrSize/arch.WordSizeBytes; i++ {
addr := Word(i * arch.WordSizeBytes)
require.Equal(t, Word(0xAAAAAAAA_AAAAAAAA), m.GetWord(addr))
}
data = []byte{0x11, 0x22, 0x33, 0x44}
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
require.Equal(t, Word(0x11223344_AAAAAAAA), m.GetWord(0))
for i := 1; i < PageAddrSize/arch.WordSizeBytes; i++ {
addr := Word(i * arch.WordSizeBytes)
require.Equal(t, Word(0xAAAAAAAA_AAAAAAAA), m.GetWord(addr))
}
})
t.Run("read-write", func(t *testing.T) { t.Run("read-write", func(t *testing.T) {
m := NewMemory() m := NewMemory()
m.SetWord(16, 0xAABBCCDD_EEFF1122) m.SetWord(16, 0xAABBCCDD_EEFF1122)
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -139,6 +140,50 @@ func TestMemoryReadWrite(t *testing.T) { ...@@ -139,6 +140,50 @@ func TestMemoryReadWrite(t *testing.T) {
require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end") require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end")
}) })
t.Run("empty range", func(t *testing.T) {
m := NewMemory()
addr := Word(0xAABBCC00)
r := bytes.NewReader(nil)
pre := m.MerkleRoot()
preJSON, err := m.MarshalJSON()
require.NoError(t, err)
var preSerialized bytes.Buffer
require.NoError(t, m.Serialize(&preSerialized))
require.NoError(t, m.SetMemoryRange(addr, r))
v := m.GetWord(0)
require.Equal(t, Word(0), v)
post := m.MerkleRoot()
require.Equal(t, pre, post)
// Assert that there are no extra zero pages in serialization
postJSON, err := m.MarshalJSON()
require.NoError(t, err)
require.Equal(t, preJSON, postJSON)
var postSerialized bytes.Buffer
require.NoError(t, m.Serialize(&postSerialized))
require.Equal(t, preSerialized.Bytes(), postSerialized.Bytes())
})
t.Run("range page overlap", func(t *testing.T) {
m := NewMemory()
data := bytes.Repeat([]byte{0xAA}, PageAddrSize)
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
for i := 0; i < PageAddrSize/arch.WordSizeBytes; i++ {
addr := Word(i * arch.WordSizeBytes)
require.Equal(t, Word(0xAAAAAAAA), m.GetWord(addr))
}
data = []byte{0x11, 0x22}
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
require.Equal(t, Word(0x1122_AAAA), m.GetWord(0))
for i := 1; i < PageAddrSize/arch.WordSizeBytes; i++ {
addr := Word(i * arch.WordSizeBytes)
require.Equal(t, Word(0xAAAAAAAA), m.GetWord(addr))
}
})
t.Run("read-write", func(t *testing.T) { t.Run("read-write", func(t *testing.T) {
m := NewMemory() m := NewMemory()
m.SetWord(12, 0xAABBCCDD) m.SetWord(12, 0xAABBCCDD)
......
...@@ -91,12 +91,6 @@ func FuzzStateConsistencyMultuOp(f *testing.F) { ...@@ -91,12 +91,6 @@ func FuzzStateConsistencyMultuOp(f *testing.F) {
}) })
} }
type insn struct {
opcode uint32
expectRdReg bool
funct uint32
}
func mulOpConsistencyCheck( func mulOpConsistencyCheck(
t *testing.T, versions []VersionedVMTestCase, t *testing.T, versions []VersionedVMTestCase,
opcode uint32, expectRdReg bool, funct uint32, opcode uint32, expectRdReg bool, funct uint32,
......
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