Skip to content

Commit 8c46b7a

Browse files
committed
feat: Add new DefaultMachineRunner trait for initialization work
Previously, all different machines in ckb-vm might use slightly different initialization process. This commit introduces a new DefaultMachineRunner trait, so the same initialization & usage workflow can be used for all types implementing this trait. This is a cherry-pick of nervosnetwork#469 to the develop branch. 2 more changes are applied on the develop branch: * Since AsmCoreMachine is now constant, we can simply use AsmCoreMachine instead of `Box<AsmCoreMachine>`. * nervosnetwork#343 was mostly reverted. Upon more thinking, I think the change earlier does not make sense now. It's better we provide unified initialization for the whole machine, not just for the memory part. Due to the new InstDecoder / TraceDecoder structure introduced in develop branch, more changes are required in the develop branch than 0.24.x releases. Some of those changes include: * Assembly machines now require TraceDecoder instead of InstDecoder. This means we cannot use DefaultMachineBuilder to build DefaultMachine structures for both types of VMs. The assembly machine types require more constraints. Given this consideration, DefaultMachineBuilder is removed in this commit. 2 new types RustDefaultMachineBuilder and AsmDefaultMachineBuilder are added for 2 types of VMs respectively. * A new set of types that begin with `Abstract`, such as AbstractDefaultMachineBuilder, AbstractAsmMachine, AbstractTraceMachine are introduced. One is only expected to use those types when one wants to tweak the inst/trace decoder used. Otherwise, one is safe to pick AsmMachine/AsmDefaultMachineBuilder or TraceMachine/RustDefaultMachineBuilder. So the current workflow is like following: 1. Pick a type implementing DefaultMachineRunner traits. Most likely, one would pick either AsmMachine or TraceMachine. 2. Instantiate a core machine using DefaultMachineRunner::Inner::new. 3. Based on the type picked in step 1, use AsmDefaultMachineBuilder or RustDefaultMachineBuilder to build a default machine with possible instruction cycle func, syscalls and debugger plugin. 4. Finally, use DefaultMachineRunner::new to create the final runnable machine from the default machine. For advanced usages where the decoder is customized, one can use AbstractAsmMachine or AbstractTraceMachine type. Then use AbstractDefaultMachineBuilder to build the default machine. And finally create the runnable machine. All those types accept an additional InstDecoder / TraceDecoder trait impl that allows one to customize the decoder for optimizations.
1 parent a1d4a9e commit 8c46b7a

31 files changed

+662
-554
lines changed

.github/workflows/develop.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
ln -snf .. ckb-vm-test-suite/ckb-vm
9090
docker run --rm -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:bionic-20210804 cp -r /riscv /code/riscv
9191
cd ckb-vm-test-suite
92-
git checkout 898edc351eeb4de974ca4f0ff8d1e4943a95aecb
92+
git checkout 2be7dcb0fda7ab41932813632fd1904e8cecf1f0
9393
git submodule update --init --recursive
9494
RISCV=`pwd`/../riscv ./test.sh
9595
@@ -161,7 +161,7 @@ jobs:
161161
ln -snf .. ckb-vm-test-suite/ckb-vm
162162
docker run --rm -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:bionic-20210804 cp -r /riscv /code/riscv
163163
cd ckb-vm-test-suite
164-
git checkout 898edc351eeb4de974ca4f0ff8d1e4943a95aecb
164+
git checkout 2be7dcb0fda7ab41932813632fd1904e8cecf1f0
165165
git submodule update --init --recursive
166166
RISCV=`pwd`/../riscv ./test.sh --build-only
167167
cd ..

benches/vm_benchmark.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ extern crate criterion;
44
use bytes::Bytes;
55
#[cfg(has_asm)]
66
use ckb_vm::{
7-
decoder::build_decoder,
7+
decoder::{DefaultDecoder, InstDecoder},
88
machine::{
99
asm::{
1010
traces::{MemoizedDynamicTraceDecoder, MemoizedFixedTraceDecoder},
11-
AsmCoreMachine, AsmMachine,
11+
AbstractAsmMachine, AsmCoreMachine, AsmDefaultMachineBuilder, AsmMachine,
1212
},
13-
DefaultMachineBuilder, VERSION0, VERSION2,
13+
AbstractDefaultMachineBuilder, DefaultMachineRunner, SupportMachine, VERSION0, VERSION2,
1414
},
1515
ISA_B, ISA_IMC, ISA_MOP,
1616
};
@@ -45,7 +45,7 @@ fn asm_benchmark(c: &mut Criterion) {
4545
].into_iter().map(|a| Ok(a.into()));
4646
b.iter(|| {
4747
let asm_core = AsmCoreMachine::new(ISA_IMC, VERSION0, u64::MAX);
48-
let core = DefaultMachineBuilder::new(asm_core).build();
48+
let core = AsmDefaultMachineBuilder::new(asm_core).build();
4949
let mut machine = AsmMachine::new(core);
5050
machine.load_program(&buffer, args.clone()).unwrap();
5151
machine.run().unwrap()
@@ -66,7 +66,7 @@ fn mop_benchmark(c: &mut Criterion) {
6666
].into_iter().map(|a| Ok(a.into()));
6767
b.iter(|| {
6868
let asm_core = AsmCoreMachine::new(ISA_IMC | ISA_B | ISA_MOP, VERSION2, u64::MAX);
69-
let core = DefaultMachineBuilder::<Box<AsmCoreMachine>>::new(asm_core)
69+
let core = AsmDefaultMachineBuilder::new(asm_core)
7070
.build();
7171
let mut machine = AsmMachine::new(core);
7272
machine.load_program(&buffer, args.clone()).unwrap();
@@ -88,19 +88,19 @@ fn mop_memoized_benchmark(c: &mut Criterion) {
8888
"foo",
8989
"bar",
9090
].into_iter().map(|a| Ok(a.into()));
91-
let mut decoder = MemoizedFixedTraceDecoder::new(build_decoder::<u64>(isa, version));
91+
let mut decoder = MemoizedFixedTraceDecoder::new::<u64>(isa, version);
9292
let asm_core = AsmCoreMachine::new(isa, version, u64::MAX);
93-
let core = DefaultMachineBuilder::<Box<AsmCoreMachine>>::new(asm_core)
93+
let core = AbstractDefaultMachineBuilder::<_, MemoizedFixedTraceDecoder<DefaultDecoder>>::new(asm_core)
9494
.build();
95-
let mut machine = AsmMachine::new(core);
95+
let mut machine = AbstractAsmMachine::new(core);
9696
machine.load_program(&buffer, args.clone()).unwrap();
9797
machine.run_with_decoder(&mut decoder).unwrap();
9898

9999
b.iter(|| {
100100
let asm_core = AsmCoreMachine::new(isa, version, u64::MAX);
101-
let core = DefaultMachineBuilder::<Box<AsmCoreMachine>>::new(asm_core)
101+
let core = AbstractDefaultMachineBuilder::<_, MemoizedFixedTraceDecoder<DefaultDecoder>>::new(asm_core)
102102
.build();
103-
let mut machine = AsmMachine::new(core);
103+
let mut machine = AbstractAsmMachine::new(core);
104104
machine.load_program(&buffer, args.clone()).unwrap();
105105
decoder.clear_traces();
106106
machine.run_with_decoder(&mut decoder).unwrap()
@@ -121,19 +121,19 @@ fn mop_memoized_dynamic_benchmark(c: &mut Criterion) {
121121
"foo",
122122
"bar",
123123
].into_iter().map(|a| Ok(a.into()));
124-
let mut decoder = MemoizedDynamicTraceDecoder::new(build_decoder::<u64>(isa, version));
124+
let mut decoder = MemoizedDynamicTraceDecoder::new::<u64>(isa, version);
125125
let asm_core = AsmCoreMachine::new(isa, version, u64::MAX);
126-
let core = DefaultMachineBuilder::<Box<AsmCoreMachine>>::new(asm_core)
126+
let core = AbstractDefaultMachineBuilder::<_, MemoizedDynamicTraceDecoder<DefaultDecoder>>::new(asm_core)
127127
.build();
128-
let mut machine = AsmMachine::new(core);
128+
let mut machine = AbstractAsmMachine::new(core);
129129
machine.load_program(&buffer, args.clone()).unwrap();
130130
machine.run_with_decoder(&mut decoder).unwrap();
131131

132132
b.iter(|| {
133133
let asm_core = AsmCoreMachine::new(isa, version, u64::MAX);
134-
let core = DefaultMachineBuilder::<Box<AsmCoreMachine>>::new(asm_core)
134+
let core = AbstractDefaultMachineBuilder::<_, MemoizedDynamicTraceDecoder<DefaultDecoder>>::new(asm_core)
135135
.build();
136-
let mut machine = AsmMachine::new(core);
136+
let mut machine = AbstractAsmMachine::new(core);
137137
machine.load_program(&buffer, args.clone()).unwrap();
138138
decoder.clear_traces();
139139
machine.run_with_decoder(&mut decoder).unwrap()

definitions/src/asm.rs

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{
2-
instructions::Instruction, DEFAULT_MEMORY_SIZE, MEMORY_FRAMESIZE, MEMORY_FRAME_SHIFTS,
3-
RISCV_GENERAL_REGISTER_NUMBER, RISCV_PAGESIZE,
2+
instructions::Instruction,
3+
RISCV_GENERAL_REGISTER_NUMBER,
44
};
5-
use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};
5+
use std::alloc::{dealloc, Layout};
66

77
// The number of trace items to keep
88
pub const TRACE_SIZE: usize = 8192;
@@ -117,49 +117,6 @@ impl Drop for AsmCoreMachine {
117117
}
118118

119119
impl AsmCoreMachine {
120-
pub fn new(isa: u8, version: u32, max_cycles: u64) -> Box<AsmCoreMachine> {
121-
Self::new_with_memory(isa, version, max_cycles, DEFAULT_MEMORY_SIZE)
122-
}
123-
124-
pub fn new_with_memory(
125-
isa: u8,
126-
version: u32,
127-
max_cycles: u64,
128-
memory_size: usize,
129-
) -> Box<AsmCoreMachine> {
130-
assert_ne!(memory_size, 0);
131-
assert_eq!(memory_size % RISCV_PAGESIZE, 0);
132-
assert_eq!(memory_size % (1 << MEMORY_FRAME_SHIFTS), 0);
133-
let mut machine = unsafe {
134-
let layout = Layout::new::<AsmCoreMachine>();
135-
let raw_allocation = alloc_zeroed(layout) as *mut AsmCoreMachine;
136-
Box::from_raw(raw_allocation)
137-
};
138-
machine.max_cycles = max_cycles;
139-
if cfg!(feature = "enable-chaos-mode-by-default") {
140-
machine.chaos_mode = 1;
141-
}
142-
machine.load_reservation_address = u64::MAX;
143-
machine.version = version;
144-
machine.isa = isa;
145-
146-
machine.memory_size = memory_size as u64;
147-
machine.frames_size = (memory_size / MEMORY_FRAMESIZE) as u64;
148-
machine.flags_size = (memory_size / RISCV_PAGESIZE) as u64;
149-
150-
machine.last_read_frame = u64::MAX;
151-
machine.last_write_page = u64::MAX;
152-
153-
let memory_layout = Layout::array::<u8>(machine.memory_size as usize).unwrap();
154-
machine.memory_ptr = unsafe { alloc(memory_layout) } as u64;
155-
let flags_layout = Layout::array::<u8>(machine.flags_size as usize).unwrap();
156-
machine.flags_ptr = unsafe { alloc_zeroed(flags_layout) } as u64;
157-
let frames_layout = Layout::array::<u8>(machine.frames_size as usize).unwrap();
158-
machine.frames_ptr = unsafe { alloc_zeroed(frames_layout) } as u64;
159-
160-
machine
161-
}
162-
163120
pub fn set_max_cycles(&mut self, cycles: u64) {
164121
self.max_cycles = cycles;
165122
}

definitions/src/generate_asm_constants.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use ckb_vm_definitions::{
1111
MEMORY_FRAMESIZE, MEMORY_FRAME_PAGE_SHIFTS, MEMORY_FRAME_SHIFTS, RISCV_PAGESIZE,
1212
RISCV_PAGE_SHIFTS,
1313
};
14+
use std::alloc::{alloc, Layout};
1415
use std::mem::{size_of, zeroed};
1516

1617
macro_rules! print_inst_label {
@@ -129,7 +130,15 @@ fn main() {
129130
);
130131
println!();
131132

132-
let m: Box<AsmCoreMachine> = AsmCoreMachine::new(0, 0, 0);
133+
// We don't need a fully initialized AsmCoreMachine, only a dummy
134+
// structure here will do.
135+
let m: Box<AsmCoreMachine> = unsafe {
136+
let machine_size = std::mem::size_of::<AsmCoreMachine>();
137+
138+
let layout = Layout::array::<u8>(machine_size).unwrap();
139+
let raw_allocation = alloc(layout) as *mut AsmCoreMachine;
140+
Box::from_raw(raw_allocation)
141+
};
133142
let m_address = &*m as *const AsmCoreMachine as usize;
134143
println!(
135144
"#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_REGISTERS {}",

examples/check_real_memory.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use std::process::{id, Command};
77
#[cfg(has_asm)]
88
use ckb_vm::{
99
machine::{
10-
asm::{AsmCoreMachine, AsmMachine},
11-
DefaultMachineBuilder, VERSION0,
10+
asm::{AsmCoreMachine, AsmDefaultMachineBuilder, AsmMachine},
11+
DefaultMachineRunner, SupportMachine, VERSION0,
1212
},
1313
ISA_IMC,
1414
};
@@ -126,7 +126,7 @@ fn check_interpreter(memory_size: usize) -> Result<(), ()> {
126126
let result = run_with_memory::<u64, SparseMemory<u64>>(
127127
&Bytes::from(BIN_PATH_BUFFER),
128128
&vec![Bytes::from(BIN_NAME)],
129-
SparseMemory::new_with_memory(memory_size),
129+
memory_size,
130130
);
131131
assert!(result.is_ok());
132132
assert_eq!(result.unwrap(), 0);
@@ -146,7 +146,7 @@ fn check_falt(memory_size: usize) -> Result<(), ()> {
146146
let result = run_with_memory::<u64, FlatMemory<u64>>(
147147
&Bytes::from(BIN_PATH_BUFFER),
148148
&vec![Bytes::from(BIN_NAME)],
149-
FlatMemory::new_with_memory(memory_size),
149+
memory_size,
150150
);
151151
assert!(result.is_ok());
152152
assert_eq!(result.unwrap(), 0);
@@ -164,8 +164,13 @@ fn check_asm(memory_size: usize) -> Result<(), ()> {
164164
);
165165
println!("Base memory: {}", get_current_memory());
166166
for _ in 0..G_CHECK_LOOP {
167-
let asm_core = AsmCoreMachine::new_with_memory(ISA_IMC, VERSION0, u64::MAX, memory_size);
168-
let core = DefaultMachineBuilder::new(asm_core).build();
167+
let asm_core = <AsmCoreMachine as SupportMachine>::new_with_memory(
168+
ISA_IMC,
169+
VERSION0,
170+
u64::MAX,
171+
memory_size,
172+
);
173+
let core = AsmDefaultMachineBuilder::new(asm_core).build();
169174
let mut machine = AsmMachine::new(core);
170175
machine
171176
.load_program(
@@ -191,8 +196,13 @@ fn check_asm_in_thread(memory_size: usize) -> Result<(), ()> {
191196
);
192197
println!("Base memory: {}", get_current_memory());
193198
for _ in 0..G_CHECK_LOOP {
194-
let asm_core = AsmCoreMachine::new_with_memory(ISA_IMC, VERSION0, u64::MAX, memory_size);
195-
let core = DefaultMachineBuilder::new(asm_core).build();
199+
let asm_core = <AsmCoreMachine as SupportMachine>::new_with_memory(
200+
ISA_IMC,
201+
VERSION0,
202+
u64::MAX,
203+
memory_size,
204+
);
205+
let core = AsmDefaultMachineBuilder::new(asm_core).build();
196206
let mut machine = AsmMachine::new(core);
197207
machine
198208
.load_program(

examples/ckb_vm_runner.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use ckb_vm::cost_model::estimate_cycles;
22
use ckb_vm::registers::{A0, A7};
3-
use ckb_vm::{Bytes, CoreMachine, Memory, Register, SupportMachine, Syscalls};
3+
use ckb_vm::{
4+
Bytes, CoreMachine, DefaultMachineRunner, Memory, Register, SupportMachine, Syscalls,
5+
};
46

57
pub struct DebugSyscall {}
68

@@ -39,12 +41,12 @@ impl<Mac: SupportMachine> Syscalls<Mac> for DebugSyscall {
3941

4042
#[cfg(has_asm)]
4143
fn main_asm(code: Bytes, args: Vec<Bytes>) -> Result<(), Box<dyn std::error::Error>> {
42-
let asm_core = ckb_vm::machine::asm::AsmCoreMachine::new(
44+
let asm_core = <ckb_vm::machine::asm::AsmCoreMachine as SupportMachine>::new(
4345
ckb_vm::ISA_IMC | ckb_vm::ISA_B | ckb_vm::ISA_MOP,
4446
ckb_vm::machine::VERSION2,
4547
u64::MAX,
4648
);
47-
let core = ckb_vm::DefaultMachineBuilder::new(asm_core)
49+
let core = ckb_vm::machine::asm::AsmDefaultMachineBuilder::new(asm_core)
4850
.instruction_cycle_func(Box::new(estimate_cycles))
4951
.syscall(Box::new(DebugSyscall {}))
5052
.build();
@@ -68,7 +70,7 @@ fn main_int(code: Bytes, args: Vec<Bytes>) -> Result<(), Box<dyn std::error::Err
6870
ckb_vm::machine::VERSION2,
6971
u64::MAX,
7072
);
71-
let machine_builder = ckb_vm::DefaultMachineBuilder::new(core_machine)
73+
let machine_builder = ckb_vm::RustDefaultMachineBuilder::new(core_machine)
7274
.instruction_cycle_func(Box::new(estimate_cycles));
7375
let mut machine = machine_builder.syscall(Box::new(DebugSyscall {})).build();
7476
machine.load_program(&code, args.into_iter().map(Ok))?;

src/decoder.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ const RISCV_PAGESIZE_MASK: u64 = RISCV_PAGESIZE as u64 - 1;
1414
const INSTRUCTION_CACHE_SIZE: usize = 4096;
1515

1616
pub trait InstDecoder {
17+
fn new<R: Register>(isa: u8, version: u32) -> Self;
1718
fn decode<M: Memory>(&mut self, memory: &mut M, pc: u64) -> Result<Instruction, Error>;
1819
fn reset_instructions_cache(&mut self) -> Result<(), Error>;
1920
}
2021

21-
pub struct Decoder {
22+
pub struct DefaultDecoder {
2223
factories: Vec<InstructionFactory>,
2324
mop: bool,
2425
version: u32,
@@ -30,9 +31,10 @@ pub struct Decoder {
3031
instructions_cache: Vec<(u64, u64)>,
3132
}
3233

33-
impl Decoder {
34-
pub fn new(mop: bool, version: u32) -> Decoder {
35-
Decoder {
34+
impl DefaultDecoder {
35+
/// Creates an empty decoder with no instruction factory
36+
pub fn empty(mop: bool, version: u32) -> Self {
37+
Self {
3638
factories: vec![],
3739
mop,
3840
version,
@@ -860,7 +862,21 @@ impl Decoder {
860862
}
861863
}
862864

863-
impl InstDecoder for Decoder {
865+
impl InstDecoder for DefaultDecoder {
866+
fn new<R: Register>(isa: u8, version: u32) -> Self {
867+
let mut decoder = Self::empty(isa & ISA_MOP != 0, version);
868+
decoder.add_instruction_factory(rvc::factory::<R>);
869+
decoder.add_instruction_factory(i::factory::<R>);
870+
decoder.add_instruction_factory(m::factory::<R>);
871+
if isa & ISA_B != 0 {
872+
decoder.add_instruction_factory(b::factory::<R>);
873+
}
874+
if isa & ISA_A != 0 {
875+
decoder.add_instruction_factory(a::factory::<R>);
876+
}
877+
decoder
878+
}
879+
864880
fn decode<M: Memory>(&mut self, memory: &mut M, pc: u64) -> Result<Instruction, Error> {
865881
if self.mop {
866882
self.decode_mop(memory, pc)
@@ -874,17 +890,3 @@ impl InstDecoder for Decoder {
874890
Ok(())
875891
}
876892
}
877-
878-
pub fn build_decoder<R: Register>(isa: u8, version: u32) -> Decoder {
879-
let mut decoder = Decoder::new(isa & ISA_MOP != 0, version);
880-
decoder.add_instruction_factory(rvc::factory::<R>);
881-
decoder.add_instruction_factory(i::factory::<R>);
882-
decoder.add_instruction_factory(m::factory::<R>);
883-
if isa & ISA_B != 0 {
884-
decoder.add_instruction_factory(b::factory::<R>);
885-
}
886-
if isa & ISA_A != 0 {
887-
decoder.add_instruction_factory(a::factory::<R>);
888-
}
889-
decoder
890-
}

0 commit comments

Comments
 (0)