1 // Currently the `VMComponentContext` allocation by field looks like this: 2 // 3 // struct VMComponentContext { 4 // magic: u32, 5 // builtins: &'static VMComponentBuiltins, 6 // limits: *const VMStoreContext, 7 // flags: [VMGlobalDefinition; component.num_runtime_component_instances], 8 // task_may_block: u32, 9 // trampoline_func_refs: [VMFuncRef; component.num_trampolines], 10 // unsafe_intrinsics: [VMFuncRef; component.num_unsafe_intrinsics], 11 // lowerings: [VMLowering; component.num_lowerings], 12 // memories: [*mut VMMemoryDefinition; component.num_runtime_memories], 13 // tables: [VMTable; component.num_runtime_tables], 14 // reallocs: [*mut VMFuncRef; component.num_runtime_reallocs], 15 // post_returns: [*mut VMFuncRef; component.num_runtime_post_returns], 16 // resource_destructors: [*mut VMFuncRef; component.num_resources], 17 // } 18 19 use crate::PtrSize; 20 use crate::component::*; 21 22 /// Equivalent of `VMCONTEXT_MAGIC` except for components. 23 /// 24 /// This is stored at the start of all `VMComponentContext` structures and 25 /// double-checked on `VMComponentContext::from_opaque`. 26 pub const VMCOMPONENT_MAGIC: u32 = u32::from_le_bytes(*b"comp"); 27 28 /// Flag for the `VMComponentContext::flags` field which corresponds to the 29 /// canonical ABI flag `may_leave` 30 pub const FLAG_MAY_LEAVE: i32 = 1 << 0; 31 32 /// Runtime offsets within a `VMComponentContext` for a specific component. 33 #[derive(Debug, Clone, Copy)] 34 pub struct VMComponentOffsets<P> { 35 /// The host pointer size 36 pub ptr: P, 37 38 /// The number of lowered functions this component will be creating. 39 pub num_lowerings: u32, 40 /// The number of memories which are recorded in this component for options. 41 pub num_runtime_memories: u32, 42 /// The number of tables which are recorded in this component for options. 43 pub num_runtime_tables: u32, 44 /// The number of reallocs which are recorded in this component for options. 45 pub num_runtime_reallocs: u32, 46 /// The number of callbacks which are recorded in this component for options. 47 pub num_runtime_callbacks: u32, 48 /// The number of post-returns which are recorded in this component for options. 49 pub num_runtime_post_returns: u32, 50 /// Number of component instances internally in the component (always at 51 /// least 1). 52 pub num_runtime_component_instances: u32, 53 /// Number of cranelift-compiled trampolines required for this component. 54 pub num_trampolines: u32, 55 /// Number of `VMFuncRef`s for unsafe intrinsics within this component's 56 /// context. 57 pub num_unsafe_intrinsics: u32, 58 /// Number of resources within a component which need destructors stored. 59 pub num_resources: u32, 60 61 // precalculated offsets of various member fields 62 magic: u32, 63 builtins: u32, 64 vm_store_context: u32, 65 flags: u32, 66 task_may_block: u32, 67 trampoline_func_refs: u32, 68 intrinsic_func_refs: u32, 69 lowerings: u32, 70 memories: u32, 71 tables: u32, 72 reallocs: u32, 73 callbacks: u32, 74 post_returns: u32, 75 resource_destructors: u32, 76 size: u32, 77 } 78 79 #[inline] 80 fn align(offset: u32, align: u32) -> u32 { 81 assert!(align.is_power_of_two()); 82 (offset + (align - 1)) & !(align - 1) 83 } 84 85 impl<P: PtrSize> VMComponentOffsets<P> { 86 /// Creates a new set of offsets for the `component` specified configured 87 /// additionally for the `ptr` size specified. 88 pub fn new(ptr: P, component: &Component) -> Self { 89 let mut ret = Self { 90 ptr, 91 num_lowerings: component.num_lowerings, 92 num_runtime_memories: component.num_runtime_memories, 93 num_runtime_tables: component.num_runtime_tables, 94 num_runtime_reallocs: component.num_runtime_reallocs, 95 num_runtime_callbacks: component.num_runtime_callbacks, 96 num_runtime_post_returns: component.num_runtime_post_returns, 97 num_runtime_component_instances: component.num_runtime_component_instances, 98 num_trampolines: component.trampolines.len().try_into().unwrap(), 99 num_unsafe_intrinsics: if let Some(i) = component 100 .unsafe_intrinsics 101 .iter() 102 .rposition(|x| x.is_some()) 103 { 104 // Note: We do not currently have an indirection between "the 105 // `i`th unsafe intrinsic in the vmctx" and 106 // `UnsafeIntrinsic::from_u32(i)`, so therefore if we are 107 // compiling in *any* intrinsics, we need to include space for 108 // all of them up to the max `i` that is used. 109 // 110 // We _could_ introduce such an indirection via a map in 111 // `Component` like `PrimaryMap<UnsafeIntrinsicIndex, 112 // UnsafeIntrinsic>`, and that would allow us to densely pack 113 // intrinsics in the vmctx. However we do not do that today 114 // because there are very few unsafe intrinsics, and we do not 115 // see that changing anytime soon, so we aren't wasting much 116 // space. 117 u32::try_from(i + 1).unwrap() 118 } else { 119 0 120 }, 121 num_resources: component.num_resources, 122 magic: 0, 123 builtins: 0, 124 vm_store_context: 0, 125 flags: 0, 126 task_may_block: 0, 127 trampoline_func_refs: 0, 128 intrinsic_func_refs: 0, 129 lowerings: 0, 130 memories: 0, 131 tables: 0, 132 reallocs: 0, 133 callbacks: 0, 134 post_returns: 0, 135 resource_destructors: 0, 136 size: 0, 137 }; 138 139 // Convenience functions for checked addition and multiplication. 140 // As side effect this reduces binary size by using only a single 141 // `#[track_caller]` location for each function instead of one for 142 // each individual invocation. 143 #[inline] 144 fn cmul(count: u32, size: u8) -> u32 { 145 count.checked_mul(u32::from(size)).unwrap() 146 } 147 148 let mut next_field_offset = 0; 149 150 macro_rules! fields { 151 (size($field:ident) = $size:expr, $($rest:tt)*) => { 152 ret.$field = next_field_offset; 153 next_field_offset = next_field_offset.checked_add(u32::from($size)).unwrap(); 154 fields!($($rest)*); 155 }; 156 (align($align:expr), $($rest:tt)*) => { 157 next_field_offset = align(next_field_offset, $align); 158 fields!($($rest)*); 159 }; 160 () => {}; 161 } 162 163 fields! { 164 size(magic) = 4u32, 165 align(u32::from(ret.ptr.size())), 166 size(builtins) = ret.ptr.size(), 167 size(vm_store_context) = ret.ptr.size(), 168 align(16), 169 size(flags) = cmul(ret.num_runtime_component_instances, ret.ptr.size_of_vmglobal_definition()), 170 size(task_may_block) = ret.ptr.size_of_vmglobal_definition(), 171 align(u32::from(ret.ptr.size())), 172 size(trampoline_func_refs) = cmul(ret.num_trampolines, ret.ptr.size_of_vm_func_ref()), 173 size(intrinsic_func_refs) = cmul(ret.num_unsafe_intrinsics, ret.ptr.size_of_vm_func_ref()), 174 size(lowerings) = cmul(ret.num_lowerings, ret.ptr.size() * 2), 175 size(memories) = cmul(ret.num_runtime_memories, ret.ptr.size()), 176 size(tables) = cmul(ret.num_runtime_tables, ret.size_of_vmtable_import()), 177 size(reallocs) = cmul(ret.num_runtime_reallocs, ret.ptr.size()), 178 size(callbacks) = cmul(ret.num_runtime_callbacks, ret.ptr.size()), 179 size(post_returns) = cmul(ret.num_runtime_post_returns, ret.ptr.size()), 180 size(resource_destructors) = cmul(ret.num_resources, ret.ptr.size()), 181 } 182 183 ret.size = next_field_offset; 184 185 // This is required by the implementation of 186 // `VMComponentContext::from_opaque`. If this value changes then this 187 // location needs to be updated. 188 assert_eq!(ret.magic, 0); 189 190 return ret; 191 } 192 193 /// The size, in bytes, of the host pointer. 194 #[inline] 195 pub fn pointer_size(&self) -> u8 { 196 self.ptr.size() 197 } 198 199 /// The offset of the `magic` field. 200 #[inline] 201 pub fn magic(&self) -> u32 { 202 self.magic 203 } 204 205 /// The offset of the `builtins` field. 206 #[inline] 207 pub fn builtins(&self) -> u32 { 208 self.builtins 209 } 210 211 /// The offset of the `flags` field. 212 #[inline] 213 pub fn instance_flags(&self, index: RuntimeComponentInstanceIndex) -> u32 { 214 assert!(index.as_u32() < self.num_runtime_component_instances); 215 self.flags + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition()) 216 } 217 218 /// The offset of the `task_may_block` field. 219 pub fn task_may_block(&self) -> u32 { 220 self.task_may_block 221 } 222 223 /// The offset of the `vm_store_context` field. 224 #[inline] 225 pub fn vm_store_context(&self) -> u32 { 226 self.vm_store_context 227 } 228 229 /// The offset of the `trampoline_func_refs` field. 230 #[inline] 231 pub fn trampoline_func_refs(&self) -> u32 { 232 self.trampoline_func_refs 233 } 234 235 /// The offset of `VMFuncRef` for the `index` specified. 236 #[inline] 237 pub fn trampoline_func_ref(&self, index: TrampolineIndex) -> u32 { 238 assert!(index.as_u32() < self.num_trampolines); 239 self.trampoline_func_refs() + index.as_u32() * u32::from(self.ptr.size_of_vm_func_ref()) 240 } 241 242 /// The offset of the `unsafe_intrinsic_func_refs` field. 243 #[inline] 244 pub fn unsafe_intrinsic_func_refs(&self) -> u32 { 245 self.intrinsic_func_refs 246 } 247 248 /// The offset of the `VMFuncRef` for the `intrinsic` specified. 249 #[inline] 250 pub fn unsafe_intrinsic_func_ref(&self, intrinsic: UnsafeIntrinsic) -> u32 { 251 assert!(intrinsic.index() < self.num_unsafe_intrinsics); 252 self.unsafe_intrinsic_func_refs() 253 + intrinsic.index() * u32::from(self.ptr.size_of_vm_func_ref()) 254 } 255 256 /// The offset of the `lowerings` field. 257 #[inline] 258 pub fn lowerings(&self) -> u32 { 259 self.lowerings 260 } 261 262 /// The offset of the `VMLowering` for the `index` specified. 263 #[inline] 264 pub fn lowering(&self, index: LoweredIndex) -> u32 { 265 assert!(index.as_u32() < self.num_lowerings); 266 self.lowerings() + index.as_u32() * u32::from(2 * self.ptr.size()) 267 } 268 269 /// The offset of the `callee` for the `index` specified. 270 #[inline] 271 pub fn lowering_callee(&self, index: LoweredIndex) -> u32 { 272 self.lowering(index) + self.lowering_callee_offset() 273 } 274 275 /// The offset of the `data` for the `index` specified. 276 #[inline] 277 pub fn lowering_data(&self, index: LoweredIndex) -> u32 { 278 self.lowering(index) + self.lowering_data_offset() 279 } 280 281 /// The size of the `VMLowering` type 282 #[inline] 283 pub fn lowering_size(&self) -> u8 { 284 2 * self.ptr.size() 285 } 286 287 /// The offset of the `callee` field within the `VMLowering` type. 288 #[inline] 289 pub fn lowering_callee_offset(&self) -> u32 { 290 0 291 } 292 293 /// The offset of the `data` field within the `VMLowering` type. 294 #[inline] 295 pub fn lowering_data_offset(&self) -> u32 { 296 u32::from(self.ptr.size()) 297 } 298 299 /// The offset of the base of the `runtime_memories` field 300 #[inline] 301 pub fn runtime_memories(&self) -> u32 { 302 self.memories 303 } 304 305 /// The offset of the `*mut VMMemoryDefinition` for the runtime index 306 /// provided. 307 #[inline] 308 pub fn runtime_memory(&self, index: RuntimeMemoryIndex) -> u32 { 309 assert!(index.as_u32() < self.num_runtime_memories); 310 self.runtime_memories() + index.as_u32() * u32::from(self.ptr.size()) 311 } 312 313 /// The offset of the base of the `runtime_tables` field 314 #[inline] 315 pub fn runtime_tables(&self) -> u32 { 316 self.tables 317 } 318 319 /// The offset of the table for the runtime index provided. 320 #[inline] 321 pub fn runtime_table(&self, index: RuntimeTableIndex) -> u32 { 322 assert!(index.as_u32() < self.num_runtime_tables); 323 self.runtime_tables() + index.as_u32() * u32::from(self.size_of_vmtable_import()) 324 } 325 326 /// Return the size of `VMTableImport`, used here to hold the pointers to 327 /// the `VMTableDefinition` and `VMContext`. 328 #[inline] 329 pub fn size_of_vmtable_import(&self) -> u8 { 330 3 * self.pointer_size() 331 } 332 333 /// The offset of the base of the `runtime_reallocs` field 334 #[inline] 335 pub fn runtime_reallocs(&self) -> u32 { 336 self.reallocs 337 } 338 339 /// The offset of the `*mut VMFuncRef` for the runtime index 340 /// provided. 341 #[inline] 342 pub fn runtime_realloc(&self, index: RuntimeReallocIndex) -> u32 { 343 assert!(index.as_u32() < self.num_runtime_reallocs); 344 self.runtime_reallocs() + index.as_u32() * u32::from(self.ptr.size()) 345 } 346 347 /// The offset of the base of the `runtime_callbacks` field 348 #[inline] 349 pub fn runtime_callbacks(&self) -> u32 { 350 self.callbacks 351 } 352 353 /// The offset of the `*mut VMFuncRef` for the runtime index 354 /// provided. 355 #[inline] 356 pub fn runtime_callback(&self, index: RuntimeCallbackIndex) -> u32 { 357 assert!(index.as_u32() < self.num_runtime_callbacks); 358 self.runtime_callbacks() + index.as_u32() * u32::from(self.ptr.size()) 359 } 360 361 /// The offset of the base of the `runtime_post_returns` field 362 #[inline] 363 pub fn runtime_post_returns(&self) -> u32 { 364 self.post_returns 365 } 366 367 /// The offset of the `*mut VMFuncRef` for the runtime index 368 /// provided. 369 #[inline] 370 pub fn runtime_post_return(&self, index: RuntimePostReturnIndex) -> u32 { 371 assert!(index.as_u32() < self.num_runtime_post_returns); 372 self.runtime_post_returns() + index.as_u32() * u32::from(self.ptr.size()) 373 } 374 375 /// The offset of the base of the `resource_destructors` field 376 #[inline] 377 pub fn resource_destructors(&self) -> u32 { 378 self.resource_destructors 379 } 380 381 /// The offset of the `*mut VMFuncRef` for the runtime index 382 /// provided. 383 #[inline] 384 pub fn resource_destructor(&self, index: ResourceIndex) -> u32 { 385 assert!(index.as_u32() < self.num_resources); 386 self.resource_destructors() + index.as_u32() * u32::from(self.ptr.size()) 387 } 388 389 /// Return the size of the `VMComponentContext` allocation. 390 #[inline] 391 pub fn size_of_vmctx(&self) -> u32 { 392 self.size 393 } 394 } 395