1use crate::backend::TranslatedCodeSection;
2use crate::error::Error;
3use crate::microwasm;
4use crate::translate_sections;
5use cranelift_codegen::{
6 ir::{self, AbiParam, Signature as CraneliftSignature},
7 isa,
8};
9use memoffset::offset_of;
10
11use std::{convert::TryInto, mem};
12use thiserror::Error;
13use wasmparser::{FuncType, Parser, Payload, Type};
14
15pub trait AsValueType {
16 const TYPE: Type;
17}
18
19pub trait TypeList {
20 const TYPE_LIST: &'static [Type];
21}
22
23impl<T> TypeList for T
24where
25 T: AsValueType,
26{
27 const TYPE_LIST: &'static [Type] = &[T::TYPE];
28}
29
30impl AsValueType for i32 {
31 const TYPE: Type = Type::I32;
32}
33impl AsValueType for i64 {
34 const TYPE: Type = Type::I64;
35}
36impl AsValueType for u32 {
37 const TYPE: Type = Type::I32;
38}
39impl AsValueType for u64 {
40 const TYPE: Type = Type::I64;
41}
42impl AsValueType for f32 {
43 const TYPE: Type = Type::F32;
44}
45impl AsValueType for f64 {
46 const TYPE: Type = Type::F64;
47}
48
49pub trait FunctionArgs<O> {
50 type FuncType;
51
52 unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> O;
53 fn into_func(start: *const u8) -> Self::FuncType;
54}
55
56type VmCtxPtr = u64;
57
58macro_rules! impl_function_args {
59 ($first:ident $(, $rest:ident)*) => {
60 impl<Output, $first, $($rest),*> FunctionArgs<Output> for ($first, $($rest),*) {
61 type FuncType = unsafe extern "sysv64" fn(VmCtxPtr, $first $(, $rest)*) -> Output;
62
63 #[allow(non_snake_case)]
64 unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output {
65 let ($first, $($rest),*) = self;
66 func(vm_ctx as VmCtxPtr, $first $(, $rest)*)
67 }
68
69 fn into_func(start: *const u8) -> Self::FuncType {
70 unsafe { mem::transmute(start) }
71 }
72 }
73
74 impl<$first: AsValueType, $($rest: AsValueType),*> TypeList for ($first, $($rest),*) {
75 const TYPE_LIST: &'static [Type] = &[$first::TYPE, $($rest::TYPE),*];
76 }
77
78 impl_function_args!($($rest),*);
79 };
80 () => {
81 impl<Output> FunctionArgs<Output> for () {
82 type FuncType = unsafe extern "sysv64" fn(VmCtxPtr) -> Output;
83
84 unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output {
85 func(vm_ctx as VmCtxPtr)
86 }
87
88 fn into_func(start: *const u8) -> Self::FuncType {
89 unsafe { mem::transmute(start) }
90 }
91 }
92
93 impl TypeList for () {
94 const TYPE_LIST: &'static [Type] = &[];
95 }
96 };
97}
98
99impl_function_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
100
101#[derive(Default)]
102pub struct TranslatedModule {
103 translated_code_section: Option<TranslatedCodeSection>,
104 ctx: SimpleContext,
105 memory: Option<(u64, Option<u64>)>,
108}
109
110impl TranslatedModule {
111 pub fn instantiate(self) -> ExecutableModule {
112 let mem_size = self.memory.map(|limits| limits.0 as u32).unwrap_or(0) as usize;
113 let mem: BoxSlice<_> = vec![0u8; mem_size * WASM_PAGE_SIZE]
114 .into_boxed_slice()
115 .into();
116
117 let ctx = if mem.len > 0 {
118 Some(Box::new(VmCtx { mem }) as Box<VmCtx>)
119 } else {
120 None
121 };
122
123 ExecutableModule {
124 module: self,
125 context: ctx,
126 }
127 }
128
129 pub fn disassemble(&self) {
130 self.translated_code_section
131 .as_ref()
132 .expect("no code section")
133 .disassemble();
134 }
135}
136
137#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)]
138pub enum ExecutionError {
139 #[error("function index out of bounds")]
140 FuncIndexOutOfBounds,
141 #[error("type mismatch")]
142 TypeMismatch,
143}
144
145pub struct ExecutableModule {
146 module: TranslatedModule,
147 context: Option<Box<VmCtx>>,
148}
149
150impl ExecutableModule {
151 pub unsafe fn execute_func_unchecked<Args: FunctionArgs<T>, T>(
158 &self,
159 func_idx: u32,
160 args: Args,
161 ) -> T {
162 let code_section = self
163 .module
164 .translated_code_section
165 .as_ref()
166 .expect("no code section");
167 let start_buf = code_section.func_start(func_idx as usize);
168
169 args.call(
170 Args::into_func(start_buf),
171 self.context
172 .as_ref()
173 .map(|ctx| (&**ctx) as *const VmCtx as *const u8)
174 .unwrap_or(std::ptr::null()),
175 )
176 }
177
178 pub fn execute_func<Args: FunctionArgs<T> + TypeList, T: TypeList>(
179 &self,
180 func_idx: u32,
181 args: Args,
182 ) -> Result<T, ExecutionError> {
183 let module = &self.module;
184
185 if func_idx as usize >= module.ctx.func_ty_indices.len() {
186 return Err(ExecutionError::FuncIndexOutOfBounds);
187 }
188
189 let type_ = module.ctx.func_type(func_idx);
190
191 if (&type_.params[..], &type_.returns[..]) != (Args::TYPE_LIST, T::TYPE_LIST) {
193 return Err(ExecutionError::TypeMismatch);
194 }
195
196 Ok(unsafe { self.execute_func_unchecked(func_idx, args) })
197 }
198
199 pub fn disassemble(&self) {
200 self.module.disassemble();
201 }
202}
203
204struct BoxSlice<T> {
205 len: usize,
206 ptr: *mut T,
207}
208
209impl<T> From<Box<[T]>> for BoxSlice<T> {
210 fn from(mut other: Box<[T]>) -> Self {
211 let out = BoxSlice {
212 len: other.len(),
213 ptr: other.as_mut_ptr(),
214 };
215 mem::forget(other);
216 out
217 }
218}
219
220unsafe impl<T: Send> Send for BoxSlice<T> {}
221unsafe impl<T: Sync> Sync for BoxSlice<T> {}
222
223impl<T> Drop for BoxSlice<T> {
224 fn drop(&mut self) {
225 unsafe { Vec::from_raw_parts(self.ptr, self.len, self.len) };
226 }
227}
228
229type BoxByteSlice = BoxSlice<u8>;
230
231pub struct VmCtx {
232 mem: BoxByteSlice,
233}
234
235impl VmCtx {
236 pub fn offset_of_memory_ptr() -> u32 {
237 (offset_of!(VmCtx, mem) + offset_of!(BoxByteSlice, ptr))
238 .try_into()
239 .expect("Offset exceeded size of u32")
240 }
241
242 pub fn offset_of_memory_len() -> u32 {
243 (offset_of!(VmCtx, mem) + offset_of!(BoxByteSlice, len))
244 .try_into()
245 .expect("Offset exceeded size of u32")
246 }
247}
248
249#[derive(Default, Debug)]
250pub struct SimpleContext {
251 types: Vec<FuncType>,
252 func_ty_indices: Vec<u32>,
253}
254
255pub const WASM_PAGE_SIZE: usize = 65_536;
256
257pub trait Signature {
258 type Type: SigType;
259
260 fn params(&self) -> &[Self::Type];
261 fn returns(&self) -> &[Self::Type];
262}
263
264pub trait SigType {
265 fn to_microwasm_type(&self) -> microwasm::SignlessType;
266}
267
268impl SigType for ir::Type {
269 fn to_microwasm_type(&self) -> microwasm::SignlessType {
270 use crate::microwasm::{Size::*, Type::*};
271
272 if self.is_int() {
273 match self.bits() {
274 32 => Int(_32),
275 64 => Int(_64),
276 _ => unimplemented!(),
277 }
278 } else if self.is_float() {
279 match self.bits() {
280 32 => Float(_32),
281 64 => Float(_64),
282 _ => unimplemented!(),
283 }
284 } else {
285 unimplemented!()
286 }
287 }
288}
289
290impl SigType for AbiParam {
291 fn to_microwasm_type(&self) -> microwasm::SignlessType {
292 self.value_type.to_microwasm_type()
293 }
294}
295
296impl Signature for CraneliftSignature {
297 type Type = AbiParam;
298
299 fn params(&self) -> &[Self::Type] {
300 assert_eq!(self.params[0].purpose, ir::ArgumentPurpose::VMContext);
303 assert_eq!(self.call_conv, isa::CallConv::SystemV);
305 &self.params[2..]
306 }
307
308 fn returns(&self) -> &[Self::Type] {
309 assert_eq!(self.call_conv, isa::CallConv::SystemV);
310 &self.returns
311 }
312}
313
314impl SigType for wasmparser::Type {
315 fn to_microwasm_type(&self) -> microwasm::SignlessType {
316 microwasm::Type::from_wasm(*self).unwrap()
317 }
318}
319
320impl Signature for FuncType {
321 type Type = wasmparser::Type;
322
323 fn params(&self) -> &[Self::Type] {
324 &*self.params
325 }
326
327 fn returns(&self) -> &[Self::Type] {
328 &*self.returns
329 }
330}
331
332pub trait ModuleContext {
333 type Signature: Signature;
334 type GlobalType: SigType;
335
336 fn vmctx_builtin_function(&self, index: u32) -> u32;
337 fn vmctx_vmglobal_definition(&self, index: u32) -> u32;
338 fn vmctx_vmglobal_import_from(&self, index: u32) -> u32;
339 fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32;
340 fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32;
341 fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32;
342 fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32;
343 fn vmmemory_definition_base(&self) -> u8;
344 fn vmmemory_definition_current_length(&self) -> u8;
345 fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32;
346 fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32;
347 fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32;
348 fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32;
349 fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32;
350 fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32;
351 fn vmtable_definition_base(&self) -> u8;
352 fn vmtable_definition_current_elements(&self) -> u8;
353 fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32;
354 fn vmcaller_checked_anyfunc_type_index(&self) -> u8;
355 fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8;
356 fn vmcaller_checked_anyfunc_vmctx(&self) -> u8;
357 fn size_of_vmcaller_checked_anyfunc(&self) -> u8;
358
359 fn defined_table_index(&self, table_index: u32) -> Option<u32>;
360 fn defined_memory_index(&self, index: u32) -> Option<u32>;
361
362 fn defined_global_index(&self, global_index: u32) -> Option<u32>;
363 fn global_type(&self, global_index: u32) -> &Self::GlobalType;
364
365 fn func_type_index(&self, func_idx: u32) -> u32;
366 fn signature(&self, index: u32) -> &Self::Signature;
367
368 fn func_index(&self, defined_func_index: u32) -> u32;
369 fn defined_func_index(&self, func_index: u32) -> Option<u32>;
370
371 fn defined_func_type(&self, defined_func_idx: u32) -> &Self::Signature {
372 self.func_type(self.func_index(defined_func_idx))
373 }
374
375 fn func_type(&self, func_idx: u32) -> &Self::Signature {
376 self.signature(self.func_type_index(func_idx))
377 }
378
379 fn emit_memory_bounds_check(&self) -> bool {
380 true
381 }
382}
383
384impl ModuleContext for SimpleContext {
385 type Signature = FuncType;
386 type GlobalType = wasmparser::Type;
387
388 fn func_index(&self, func_idx: u32) -> u32 {
390 func_idx
391 }
392
393 fn defined_func_index(&self, func_idx: u32) -> Option<u32> {
394 Some(func_idx)
395 }
396
397 fn func_type_index(&self, func_idx: u32) -> u32 {
398 self.func_ty_indices[func_idx as usize]
399 }
400
401 fn defined_global_index(&self, _index: u32) -> Option<u32> {
402 unimplemented!()
403 }
404
405 fn global_type(&self, _global_index: u32) -> &Self::GlobalType {
406 unimplemented!()
407 }
408
409 fn signature(&self, index: u32) -> &Self::Signature {
410 &self.types[index as usize]
411 }
412
413 fn vmctx_vmglobal_definition(&self, _index: u32) -> u32 {
414 unimplemented!()
415 }
416
417 fn vmctx_vmglobal_import_from(&self, _index: u32) -> u32 {
418 unimplemented!()
419 }
420
421 fn defined_memory_index(&self, _index: u32) -> Option<u32> {
422 unimplemented!()
423 }
424
425 fn defined_table_index(&self, index: u32) -> Option<u32> {
426 Some(index)
427 }
428
429 fn vmctx_builtin_function(&self, _index: u32) -> u32 {
430 unimplemented!()
431 }
432
433 fn vmctx_vmfunction_import_body(&self, _func_index: u32) -> u32 {
434 unimplemented!()
435 }
436 fn vmctx_vmfunction_import_vmctx(&self, _func_index: u32) -> u32 {
437 unimplemented!()
438 }
439
440 fn vmctx_vmtable_import_from(&self, _table_index: u32) -> u32 {
441 unimplemented!()
442 }
443
444 fn vmctx_vmmemory_definition(&self, _defined_memory_index: u32) -> u32 {
445 unimplemented!()
446 }
447 fn vmctx_vmmemory_import_from(&self, _memory_index: u32) -> u32 {
448 unimplemented!()
449 }
450 fn vmmemory_definition_base(&self) -> u8 {
451 unimplemented!()
452 }
453 fn vmmemory_definition_current_length(&self) -> u8 {
454 unimplemented!()
455 }
456 fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
457 assert_eq!(defined_memory_index, 0);
458 VmCtx::offset_of_memory_ptr()
459 }
460
461 fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
462 assert_eq!(defined_memory_index, 0);
463 VmCtx::offset_of_memory_len()
464 }
465
466 fn vmctx_vmtable_definition(&self, _defined_table_index: u32) -> u32 {
467 unimplemented!()
468 }
469
470 fn vmctx_vmtable_definition_base(&self, _defined_table_index: u32) -> u32 {
471 unimplemented!()
472 }
473
474 fn vmctx_vmtable_definition_current_elements(&self, _defined_table_index: u32) -> u32 {
475 unimplemented!()
476 }
477
478 fn vmtable_definition_base(&self) -> u8 {
479 unimplemented!()
480 }
481
482 fn vmtable_definition_current_elements(&self) -> u8 {
483 unimplemented!()
484 }
485
486 fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
487 unimplemented!()
488 }
489
490 fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
491 unimplemented!()
492 }
493
494 fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
495 unimplemented!()
496 }
497
498 fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
499 unimplemented!()
500 }
501
502 fn vmctx_vmshared_signature_id(&self, _signature_idx: u32) -> u32 {
503 unimplemented!()
504 }
505
506 }
508
509pub fn translate(data: &[u8]) -> Result<ExecutableModule, Error> {
510 translate_only(data).map(|m| m.instantiate())
511}
512
513pub fn translate_only(data: &[u8]) -> Result<TranslatedModule, Error> {
515 let mut output = TranslatedModule::default();
516
517 for payload in Parser::new(0).parse_all(data) {
518 match payload? {
519 Payload::TypeSection(s) => output.ctx.types = translate_sections::type_(s)?,
520 Payload::ImportSection(s) => translate_sections::import(s)?,
521 Payload::FunctionSection(s) => {
522 output.ctx.func_ty_indices = translate_sections::function(s)?;
523 }
524 Payload::TableSection(s) => {
525 translate_sections::table(s)?;
526 }
527 Payload::MemorySection(s) => {
528 let mem = translate_sections::memory(s)?;
529
530 if mem.len() > 1 {
531 return Err(Error::Input(
532 "Multiple memory sections not yet implemented".to_string(),
533 ));
534 }
535
536 if !mem.is_empty() {
537 let mem = mem[0];
538 if Some(mem.initial) != mem.maximum {
539 return Err(Error::Input(
540 "Custom memory limits not supported in lightbeam".to_string(),
541 ));
542 }
543 output.memory = Some((mem.initial, mem.maximum));
544 }
545 }
546 Payload::GlobalSection(s) => {
547 translate_sections::global(s)?;
548 }
549 Payload::ExportSection(s) => {
550 translate_sections::export(s)?;
551 }
552 Payload::StartSection { func, .. } => {
553 translate_sections::start(func)?;
554 }
555 Payload::ElementSection(s) => {
556 translate_sections::element(s)?;
557 }
558 Payload::DataSection(s) => {
559 translate_sections::data(s)?;
560 }
561 Payload::CodeSectionStart { .. }
562 | Payload::CustomSection { .. }
563 | Payload::Version { .. } => {}
564
565 other => unimplemented!("can't translate {:?}", other),
566 }
567 }
568
569 Ok(output)
570}