Skip to content

Commit 9e59609

Browse files
mbaxterInphi
authored andcommitted
cannon: Simplify futex handling (ethereum-optimism#13754)
* cannon: Simplify futex handling * cannon: Cut envar and go ahead and implement new futex behavior directly * cannon: Cut now unused onWaitComplete() method * cannon: Update single-threaded mips call to getSyscallArgs * cannon: Update contracts to match go vm * cannon: Update solidity tests * cannon: Reconcile MIPS.sol version with latest release (1.2.1) * cannon: Update differential tests * cannon: Cut wakeup field * cannon: Remove thread futex fields * cannon: Fix const name * cannon: Add new cannon state versions * cannon: Cut wakeup stats * cannon: Fix ParseStateVersion(), add test * cannon: Reenable evm validation in tests * cannon: Temporarily use ad hoc cannon release * cannon: Run semver lock * cannon: Fix semver comment, run semver-lock * cannon: Cut unused constants * cannon: Use latest cannon release in op-stack-go dockerfile --------- Co-authored-by: inphi <[email protected]>
1 parent 8c9dc18 commit 9e59609

File tree

38 files changed

+257
-1358
lines changed

38 files changed

+257
-1358
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,14 @@ cannon-prestate: op-program cannon ## Generates prestate using cannon and op-pro
152152
mv op-program/bin/0.json op-program/bin/prestate-proof.json
153153
.PHONY: cannon-prestate
154154

155-
cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the multithreaded64-2 cannon format
156-
./cannon/bin/cannon load-elf --type multithreaded64-2 --path op-program/bin/op-program-client64.elf --out op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json
155+
cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the latest 64-bit multithreaded cannon format
156+
./cannon/bin/cannon load-elf --type multithreaded64-3 --path op-program/bin/op-program-client64.elf --out op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json
157157
./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json --proof-fmt 'op-program/bin/%d-mt.json' --output ""
158158
mv op-program/bin/0-mt.json op-program/bin/prestate-proof-mt.json
159159
.PHONY: cannon-prestate-mt
160160

161-
cannon-prestate-interop: op-program cannon ## Generates interop prestate using cannon and op-program in the multithreaded64-2 cannon format
162-
./cannon/bin/cannon load-elf --type multithreaded64-2 --path op-program/bin/op-program-client-interop.elf --out op-program/bin/prestate-interop.bin.gz --meta op-program/bin/meta-interop.json
161+
cannon-prestate-interop: op-program cannon ## Generates interop prestate using cannon and op-program in the latest 64-bit multithreaded cannon format
162+
./cannon/bin/cannon load-elf --type multithreaded64-3 --path op-program/bin/op-program-client-interop.elf --out op-program/bin/prestate-interop.bin.gz --meta op-program/bin/meta-interop.json
163163
./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate-interop.bin.gz --meta op-program/bin/meta-interop.json --proof-fmt 'op-program/bin/%d-interop.json' --output ""
164164
mv op-program/bin/0-interop.json op-program/bin/prestate-proof-interop.json
165165
.PHONY: cannon-prestate-interop

cannon/Makefile

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ cannon64-impl:
3131
# It should build the individual versions of cannons and copy them into place in hte multicannon/embeds directory
3232
# Ideally, preserve backwards compatibility with this behaviour but if it needs to change, build-legacy-cannons.sh will
3333
# need to be updated to account for different behaviours in different versions.
34+
# Each embed is suffixed with the latest `StateVersion` number corresponding to the target VM and architecture.
3435
cannon-embeds: cannon32-impl cannon64-impl
35-
# singlethreaded-v2
36+
# 32-bit singlethreaded vm
3637
@cp bin/cannon32-impl ./multicannon/embeds/cannon-2
37-
# multithreaded
38-
@cp bin/cannon32-impl ./multicannon/embeds/cannon-1
39-
# 64-bit multithreaded v2
40-
@cp bin/cannon64-impl ./multicannon/embeds/cannon-4
38+
# 32-bit multithreaded vm
39+
@cp bin/cannon32-impl ./multicannon/embeds/cannon-5
40+
# 64-bit multithreaded vm
41+
@cp bin/cannon64-impl ./multicannon/embeds/cannon-6
4142

4243
cannon: cannon-embeds
4344
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon ./multicannon/

cannon/cmd/load_elf.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func LoadELF(ctx *cli.Context) error {
8080
}
8181
return program.PatchStack(state)
8282
}
83-
case versions.VersionMultiThreaded, versions.VersionMultiThreaded64_v2:
83+
case versions.VersionMultiThreaded_v2, versions.VersionMultiThreaded64_v3:
8484
createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
8585
return program.LoadELF(f, multithreaded.CreateInitialState)
8686
}

cannon/mipsevm/debug.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@ type DebugInfo struct {
1414
MaxStepsBetweenLLAndSC uint64 `json:"max_steps_between_ll_and_sc"`
1515
ReservationInvalidationCount uint64 `json:"reservation_invalidation_count"`
1616
ForcedPreemptionCount uint64 `json:"forced_preemption_count"`
17-
FailedWakeupCount uint64 `json:"failed_wakeup_count"`
1817
IdleStepCountThread0 uint64 `json:"idle_step_count_thread_0"`
1918
}

cannon/mipsevm/debug_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ func TestDebugInfo_Serialization(t *testing.T) {
2323
MaxStepsBetweenLLAndSC: 7,
2424
ReservationInvalidationCount: 8,
2525
ForcedPreemptionCount: 9,
26-
FailedWakeupCount: 10,
2726
IdleStepCountThread0: math.MaxUint64,
2827
}
2928

cannon/mipsevm/exec/mips_syscalls.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,8 @@ const (
4242

4343
// SysFutex-related constants
4444
const (
45-
FutexWaitPrivate = 128
46-
FutexWakePrivate = 129
47-
FutexTimeoutSteps = 10_000
48-
FutexNoTimeout = ^uint64(0)
49-
FutexEmptyAddr = ^Word(0)
45+
FutexWaitPrivate = 128
46+
FutexWakePrivate = 129
5047
)
5148

5249
// SysClone flags
@@ -99,15 +96,14 @@ const (
9996
ClockGettimeMonotonicFlag = 1
10097
)
10198

102-
func GetSyscallArgs(registers *[32]Word) (syscallNum, a0, a1, a2, a3 Word) {
99+
func GetSyscallArgs(registers *[32]Word) (syscallNum, a0, a1, a2 Word) {
103100
syscallNum = registers[register.RegSyscallNum] // v0
104101

105102
a0 = registers[register.RegSyscallParam1]
106103
a1 = registers[register.RegSyscallParam2]
107104
a2 = registers[register.RegSyscallParam3]
108-
a3 = registers[register.RegSyscallParam4]
109105

110-
return syscallNum, a0, a1, a2, a3
106+
return syscallNum, a0, a1, a2
111107
}
112108

113109
func HandleSysMmap(a0, a1, heap Word) (v0, v1, newHeap Word) {

cannon/mipsevm/multithreaded/mips.go

Lines changed: 14 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Word = arch.Word
2020
func (m *InstrumentedState) handleSyscall() error {
2121
thread := m.state.GetCurrentThread()
2222

23-
syscallNum, a0, a1, a2, a3 := exec.GetSyscallArgs(m.state.GetRegistersRef())
23+
syscallNum, a0, a1, a2 := exec.GetSyscallArgs(m.state.GetRegistersRef())
2424
v0 := Word(0)
2525
v1 := Word(0)
2626

@@ -43,12 +43,9 @@ func (m *InstrumentedState) handleSyscall() error {
4343
v0 = m.state.NextThreadId
4444
v1 = 0
4545
newThread := &ThreadState{
46-
ThreadId: m.state.NextThreadId,
47-
ExitCode: 0,
48-
Exited: false,
49-
FutexAddr: exec.FutexEmptyAddr,
50-
FutexVal: 0,
51-
FutexTimeoutStep: 0,
46+
ThreadId: m.state.NextThreadId,
47+
ExitCode: 0,
48+
Exited: false,
5249
Cpu: mipsevm.CpuScalars{
5350
PC: thread.Cpu.NextPC,
5451
NextPC: thread.Cpu.NextPC + 4,
@@ -119,37 +116,18 @@ func (m *InstrumentedState) handleSyscall() error {
119116
v0 = exec.SysErrorSignal
120117
v1 = exec.MipsEAGAIN
121118
} else {
122-
thread.FutexAddr = effFutexAddr
123-
thread.FutexVal = targetVal
124-
if a3 == 0 {
125-
thread.FutexTimeoutStep = exec.FutexNoTimeout
126-
} else {
127-
thread.FutexTimeoutStep = m.state.Step + exec.FutexTimeoutSteps
128-
}
129-
// Leave cpu scalars as-is. This instruction will be completed by `onWaitComplete`
119+
m.syscallYield(thread)
130120
return nil
131121
}
132122
case exec.FutexWakePrivate:
133-
// Trigger a wakeup traversal
134-
m.state.Wakeup = effFutexAddr
135-
// Don't indicate to the program that we've woken up a waiting thread, as there are no guarantees.
136-
// The woken up thread should indicate this in userspace.
137-
v0 = 0
138-
v1 = 0
139-
exec.HandleSyscallUpdates(&thread.Cpu, &thread.Registers, v0, v1)
140-
m.preemptThread(thread)
141-
m.state.TraverseRight = len(m.state.LeftThreadStack) == 0
142-
m.statsTracker.trackWakeupTraversalStart()
123+
m.syscallYield(thread)
143124
return nil
144125
default:
145126
v0 = exec.SysErrorSignal
146127
v1 = exec.MipsEINVAL
147128
}
148129
case arch.SysSchedYield, arch.SysNanosleep:
149-
v0 = 0
150-
v1 = 0
151-
exec.HandleSyscallUpdates(&thread.Cpu, &thread.Registers, v0, v1)
152-
m.preemptThread(thread)
130+
m.syscallYield(thread)
153131
return nil
154132
case arch.SysOpen:
155133
v0 = exec.SysErrorSignal
@@ -225,6 +203,13 @@ func (m *InstrumentedState) handleSyscall() error {
225203
return nil
226204
}
227205

206+
func (m *InstrumentedState) syscallYield(thread *ThreadState) {
207+
v0 := Word(0)
208+
v1 := Word(0)
209+
exec.HandleSyscallUpdates(&thread.Cpu, &thread.Registers, v0, v1)
210+
m.preemptThread(thread)
211+
}
212+
228213
func (m *InstrumentedState) mipsStep() error {
229214
err := m.doMipsStep()
230215
if err != nil {
@@ -249,56 +234,12 @@ func (m *InstrumentedState) doMipsStep() error {
249234
m.state.Step += 1
250235
thread := m.state.GetCurrentThread()
251236

252-
// During wakeup traversal, search for the first thread blocked on the wakeup address.
253-
// Don't allow regular execution until we have found such a thread or else we have visited all threads.
254-
if m.state.Wakeup != exec.FutexEmptyAddr {
255-
// We are currently performing a wakeup traversal
256-
if m.state.Wakeup == thread.FutexAddr {
257-
// We found a target thread, resume normal execution and process this thread
258-
m.state.Wakeup = exec.FutexEmptyAddr
259-
} else {
260-
// This is not the thread we're looking for, move on
261-
traversingRight := m.state.TraverseRight
262-
changedDirections := m.preemptThread(thread)
263-
if traversingRight && changedDirections {
264-
// We started the wakeup traversal walking left and we've now walked all the way right
265-
// We have therefore visited all threads and can resume normal thread execution
266-
m.state.Wakeup = exec.FutexEmptyAddr
267-
}
268-
}
269-
return nil
270-
}
271-
272237
if thread.Exited {
273238
m.popThread()
274239
m.stackTracker.DropThread(thread.ThreadId)
275240
return nil
276241
}
277242

278-
// check if thread is blocked on a futex
279-
if thread.FutexAddr != exec.FutexEmptyAddr {
280-
// if set, then check futex
281-
// check timeout first
282-
if m.state.Step > thread.FutexTimeoutStep {
283-
// timeout! Allow execution
284-
m.onWaitComplete(thread, true)
285-
return nil
286-
} else {
287-
futexVal := m.getFutexValue(thread.FutexAddr)
288-
if thread.FutexVal == futexVal {
289-
// still got expected value, continue sleeping, try next thread.
290-
m.preemptThread(thread)
291-
m.statsTracker.trackWakeupFail()
292-
return nil
293-
} else {
294-
// wake thread up, the value at its address changed!
295-
// Userspace can turn thread back to sleep if it was too sporadic.
296-
m.onWaitComplete(thread, false)
297-
return nil
298-
}
299-
}
300-
}
301-
302243
if m.state.StepsSinceLastContextSwitch >= exec.SchedQuantum {
303244
// Force a context switch as this thread has been active too long
304245
if m.state.ThreadCount() > 1 {
@@ -412,24 +353,6 @@ func (m *InstrumentedState) handleRMWOps(insn, opcode uint32) error {
412353
return exec.HandleRd(m.state.getCpuRef(), m.state.GetRegistersRef(), rtReg, retVal, true)
413354
}
414355

415-
func (m *InstrumentedState) onWaitComplete(thread *ThreadState, isTimedOut bool) {
416-
// Note: no need to reset m.state.Wakeup. If we're here, the Wakeup field has already been reset
417-
// Clear the futex state
418-
thread.FutexAddr = exec.FutexEmptyAddr
419-
thread.FutexVal = 0
420-
thread.FutexTimeoutStep = 0
421-
422-
// Complete the FUTEX_WAIT syscall
423-
v0 := Word(0)
424-
v1 := Word(0)
425-
if isTimedOut {
426-
v0 = exec.SysErrorSignal
427-
v1 = exec.MipsETIMEDOUT
428-
}
429-
exec.HandleSyscallUpdates(&thread.Cpu, &thread.Registers, v0, v1)
430-
m.statsTracker.trackWakeup()
431-
}
432-
433356
func (m *InstrumentedState) preemptThread(thread *ThreadState) bool {
434357
// Pop thread from the current stack and push to the other stack
435358
if m.state.TraverseRight {

cannon/mipsevm/multithreaded/state.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212

1313
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
1414
"github.com/ethereum-optimism/optimism/cannon/mipsevm/arch"
15-
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
1615
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
1716
"github.com/ethereum-optimism/optimism/op-service/serialize"
1817
)
@@ -30,13 +29,12 @@ const (
3029
EXITED_WITNESS_OFFSET = EXITCODE_WITNESS_OFFSET + 1
3130
STEP_WITNESS_OFFSET = EXITED_WITNESS_OFFSET + 1
3231
STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET = STEP_WITNESS_OFFSET + 8
33-
WAKEUP_WITNESS_OFFSET = STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET + 8
34-
TRAVERSE_RIGHT_WITNESS_OFFSET = WAKEUP_WITNESS_OFFSET + arch.WordSizeBytes
32+
TRAVERSE_RIGHT_WITNESS_OFFSET = STEPS_SINCE_CONTEXT_SWITCH_WITNESS_OFFSET + 8
3533
LEFT_THREADS_ROOT_WITNESS_OFFSET = TRAVERSE_RIGHT_WITNESS_OFFSET + 1
3634
RIGHT_THREADS_ROOT_WITNESS_OFFSET = LEFT_THREADS_ROOT_WITNESS_OFFSET + 32
3735
THREAD_ID_WITNESS_OFFSET = RIGHT_THREADS_ROOT_WITNESS_OFFSET + 32
3836

39-
// 172 and 196 bytes for 32 and 64-bit respectively
37+
// 168 and 188 bytes for 32 and 64-bit respectively
4038
STATE_WITNESS_SIZE = THREAD_ID_WITNESS_OFFSET + arch.WordSizeBytes
4139
)
4240

@@ -64,7 +62,6 @@ type State struct {
6462

6563
Step uint64
6664
StepsSinceLastContextSwitch uint64
67-
Wakeup Word
6865

6966
TraverseRight bool
7067
LeftThreadStack []*ThreadState
@@ -89,7 +86,6 @@ func CreateEmptyState() *State {
8986
ExitCode: 0,
9087
Exited: false,
9188
Step: 0,
92-
Wakeup: exec.FutexEmptyAddr,
9389
TraverseRight: false,
9490
LeftThreadStack: []*ThreadState{initThread},
9591
RightThreadStack: []*ThreadState{},
@@ -215,7 +211,6 @@ func (s *State) EncodeWitness() ([]byte, common.Hash) {
215211

216212
out = binary.BigEndian.AppendUint64(out, s.Step)
217213
out = binary.BigEndian.AppendUint64(out, s.StepsSinceLastContextSwitch)
218-
out = arch.ByteOrderWord.AppendWord(out, s.Wakeup)
219214

220215
leftStackRoot := s.getLeftThreadStackRoot()
221216
rightStackRoot := s.getRightThreadStackRoot()
@@ -262,7 +257,6 @@ func (s *State) ThreadCount() int {
262257
// Exited uint8 - 0 for false, 1 for true
263258
// Step uint64
264259
// StepsSinceLastContextSwitch uint64
265-
// Wakeup Word
266260
// TraverseRight uint8 - 0 for false, 1 for true
267261
// NextThreadId Word
268262
// len(LeftThreadStack) Word
@@ -307,9 +301,6 @@ func (s *State) Serialize(out io.Writer) error {
307301
if err := bout.WriteUInt(s.StepsSinceLastContextSwitch); err != nil {
308302
return err
309303
}
310-
if err := bout.WriteUInt(s.Wakeup); err != nil {
311-
return err
312-
}
313304
if err := bout.WriteBool(s.TraverseRight); err != nil {
314305
return err
315306
}
@@ -376,9 +367,6 @@ func (s *State) Deserialize(in io.Reader) error {
376367
if err := bin.ReadUInt(&s.StepsSinceLastContextSwitch); err != nil {
377368
return err
378369
}
379-
if err := bin.ReadUInt(&s.Wakeup); err != nil {
380-
return err
381-
}
382370
if err := bin.ReadBool(&s.TraverseRight); err != nil {
383371
return err
384372
}

0 commit comments

Comments
 (0)