1 use crate::ModuleContext;
2 use crate::component::info::{Accessor, RawSection};
3 use crate::component::{ComponentContext, WIZER_INSTANCE};
4 use wasm_encoder::reencode::{Reencode, RoundtripReencoder};
5 use wasmtime::{Result, bail};
6
7 /// Instrumentation phase of wizening a component.
8 ///
9 /// This is similar to the core wasm wizening instrumentation but operates at
10 /// the component level. This notably handles multiple instances and multiple
11 /// globals/memories across these instances. The general idea of the
12 /// instrumented component is:
13 ///
14 /// * All core modules are instrumented with typical wizer instrumentation (e.g.
15 /// `__wizer_*` exports of all internal state).
16 /// * A core module is then generated which accesses all of these exports in the
17 /// form of functions.
18 /// * This core module is instantiated and lifted in a component instance which
19 /// contains exported functions for accessing all of the various pieces of
20 /// state of the module.
21 /// * The lifted instance is exported under the `WIZER_INSTANCE` name.
22 ///
23 /// The main goal is to reuse the core wasm instrumentation as much as possible
24 /// here, and then the only remaining question is how to plumb all the core wasm
25 /// state out of the component through WIT.
instrument(component: &mut ComponentContext<'_>) -> Result<Vec<u8>>26 pub(crate) fn instrument(component: &mut ComponentContext<'_>) -> Result<Vec<u8>> {
27 let mut encoder = wasm_encoder::Component::new();
28
29 // First pass through all sections as-is, ensuring that module sections are
30 // the instrumented version of the module.
31 for section in component.sections.iter_mut() {
32 match section {
33 RawSection::Raw(raw) => {
34 encoder.section(raw);
35 }
36 RawSection::Module(module) => {
37 let wasm = crate::instrument::instrument(module);
38 encoder.section(&wasm_encoder::RawSection {
39 id: wasm_encoder::ComponentSectionId::CoreModule as u8,
40 data: &wasm,
41 });
42 }
43 }
44 }
45
46 // Build the accessor module and append it with this helper.
47 let mut builder = AccessorBuilder {
48 component,
49 accessor_types: Default::default(),
50 accessor_imports: Default::default(),
51 accessor_functions: Default::default(),
52 accessor_code: Default::default(),
53 accessor_exports: Default::default(),
54 accessor_nglobals: 0,
55 accessor_nmemories: 0,
56 instances_to_instantiate_with: Vec::new(),
57 accessors: Vec::new(),
58 extra_types: Default::default(),
59 extra_aliases: Default::default(),
60 extra_canonicals: Default::default(),
61 accessor_instance_export_items: Vec::new(),
62 extra_core_funcs: 0,
63 };
64 builder.build(&mut encoder)?;
65 component.accessors = Some(builder.accessors);
66
67 Ok(encoder.finish())
68 }
69
70 struct AccessorBuilder<'a> {
71 component: &'a ComponentContext<'a>,
72
73 // Sections that are used to create the "accessor" module which is a bunch
74 // of functions that reads the internal state of all other instances in this
75 // component.
76 accessor_types: wasm_encoder::TypeSection,
77 accessor_imports: wasm_encoder::ImportSection,
78 accessor_functions: wasm_encoder::FunctionSection,
79 accessor_code: wasm_encoder::CodeSection,
80 accessor_exports: wasm_encoder::ExportSection,
81 accessor_nglobals: u32,
82 accessor_nmemories: u32,
83
84 // Arguments to the instantiation of the accessor module.
85 instances_to_instantiate_with: Vec<(u32, String)>,
86
87 // All accessor functions generated for all instance internal state.
88 accessors: Vec<Accessor>,
89
90 // Sections that are appended to the component as part of the
91 // instrumentation. This is the implementation detail of lifting all the
92 // functions in the "accessor" module.
93 extra_types: wasm_encoder::ComponentTypeSection,
94 extra_aliases: wasm_encoder::ComponentAliasSection,
95 extra_core_funcs: u32,
96 extra_canonicals: wasm_encoder::CanonicalFunctionSection,
97 accessor_instance_export_items: Vec<(String, wasm_encoder::ComponentExportKind, u32)>,
98 }
99
100 impl AccessorBuilder<'_> {
build(&mut self, encoder: &mut wasm_encoder::Component) -> Result<()>101 fn build(&mut self, encoder: &mut wasm_encoder::Component) -> Result<()> {
102 for (module_index, module) in self.component.core_modules() {
103 let instance_index = match self.component.core_instantiations.get(&module_index) {
104 Some(i) => *i,
105 None => continue,
106 };
107 self.add_core_instance(module_index, module, instance_index)?;
108 }
109
110 self.finish(encoder);
111 Ok(())
112 }
113
add_core_instance( &mut self, module_index: u32, module: &ModuleContext<'_>, instance_index: u32, ) -> Result<()>114 fn add_core_instance(
115 &mut self,
116 module_index: u32,
117 module: &ModuleContext<'_>,
118 instance_index: u32,
119 ) -> Result<()> {
120 let instance_import_name = instance_index.to_string();
121
122 for (_, ty, name) in module.defined_globals() {
123 let name = match name {
124 Some(n) => n,
125 None => continue,
126 };
127
128 let accessor_export_name =
129 self.add_core_instance_global(&instance_import_name, name, ty)?;
130 self.accessors.push(Accessor::Global {
131 module_index,
132 accessor_export_name,
133 ty: ty.content_type,
134 core_export_name: name.to_string(),
135 });
136 self.accessor_nglobals += 1;
137 }
138
139 let defined_memory_exports = module.defined_memory_exports.as_ref().unwrap();
140 for ((_, ty), name) in module.defined_memories().zip(defined_memory_exports) {
141 let accessor_export_name =
142 self.add_core_instance_memory(instance_index, &instance_import_name, name, ty);
143 self.accessors.push(Accessor::Memory {
144 module_index,
145 accessor_export_name,
146 core_export_name: name.to_string(),
147 });
148 self.accessor_nmemories += 1;
149 }
150
151 self.instances_to_instantiate_with
152 .push((instance_index, instance_import_name));
153
154 Ok(())
155 }
156
add_core_instance_global( &mut self, instance_import_name: &str, global_export_name: &str, global_ty: wasmparser::GlobalType, ) -> Result<String>157 fn add_core_instance_global(
158 &mut self,
159 instance_import_name: &str,
160 global_export_name: &str,
161 global_ty: wasmparser::GlobalType,
162 ) -> Result<String> {
163 // Import the global and then define a function which returns the
164 // current value of the global.
165 self.accessor_imports.import(
166 &instance_import_name,
167 global_export_name,
168 RoundtripReencoder.global_type(global_ty).unwrap(),
169 );
170 let type_index = self.accessor_types.len();
171 self.accessor_types.ty().function(
172 [],
173 [RoundtripReencoder.val_type(global_ty.content_type).unwrap()],
174 );
175 self.accessor_functions.function(type_index);
176
177 // Accessing a global is pretty easy, it's just `global.get`
178 let mut function = wasm_encoder::Function::new([]);
179 let mut ins = function.instructions();
180 ins.global_get(self.accessor_nglobals);
181 ins.end();
182 self.accessor_code.function(&function);
183
184 let prim = match global_ty.content_type {
185 wasmparser::ValType::I32 => wasm_encoder::PrimitiveValType::S32,
186 wasmparser::ValType::I64 => wasm_encoder::PrimitiveValType::S64,
187 wasmparser::ValType::F32 => wasm_encoder::PrimitiveValType::F32,
188 wasmparser::ValType::F64 => wasm_encoder::PrimitiveValType::F64,
189 wasmparser::ValType::V128 => bail!("component wizening does not support v128 globals"),
190 wasmparser::ValType::Ref(_) => unreachable!(),
191 };
192 let lift_type_index = self.component.types + self.extra_types.len();
193 self.extra_types
194 .function()
195 .params::<_, wasm_encoder::ComponentValType>([])
196 .result(Some(wasm_encoder::ComponentValType::Primitive(prim)));
197 Ok(self.lift_accessor("global", lift_type_index, &[]))
198 }
199
add_core_instance_memory( &mut self, instance_index: u32, instance_import_name: &str, memory_export_name: &str, memory_ty: wasmparser::MemoryType, ) -> String200 fn add_core_instance_memory(
201 &mut self,
202 instance_index: u32,
203 instance_import_name: &str,
204 memory_export_name: &str,
205 memory_ty: wasmparser::MemoryType,
206 ) -> String {
207 self.accessor_imports.import(
208 &instance_import_name,
209 memory_export_name,
210 RoundtripReencoder.memory_type(memory_ty).unwrap(),
211 );
212 let type_index = self.accessor_types.len();
213 self.accessor_types
214 .ty()
215 .function([], [wasm_encoder::ValType::I32]);
216 self.accessor_functions.function(type_index);
217
218 // Accessing a linear memory is more subtle than a global. We're
219 // returning a `list<u8>` in WIT but to do so we have to store the
220 // ptr/length in memory itself. To work around this the memory is grown
221 // by a single page to ensure we don't tamper with the original image,
222 // and then in this new page the ptr/len are stored. The base pointer is
223 // always 0 and the length is the size of memory prior to the growth.
224 let mut function = wasm_encoder::Function::new([(1, wasm_encoder::ValType::I32)]);
225 let mut ins = function.instructions();
226
227 // Grow memory by 1 page, and trap if the growth failed.
228 let pages_to_grow_by = match memory_ty.page_size_log2 {
229 Some(0) => 8,
230 Some(16) | None => 1,
231 _ => unreachable!(),
232 };
233 ins.i32_const(pages_to_grow_by);
234 ins.memory_grow(self.accessor_nmemories);
235 ins.local_tee(0);
236 ins.i32_const(-1);
237 ins.i32_eq();
238 ins.if_(wasm_encoder::BlockType::Empty);
239 ins.unreachable();
240 ins.end();
241
242 // Update our one local as the full byte length of memory.
243 ins.local_get(0);
244 ins.i32_const(memory_ty.page_size_log2.unwrap_or(16).cast_signed());
245 ins.i32_shl();
246 ins.local_set(0);
247
248 let memarg = |offset| wasm_encoder::MemArg {
249 align: 2,
250 offset,
251 memory_index: self.accessor_nmemories,
252 };
253 // Store the ptr/len into the page that was just allocated
254 ins.local_get(0);
255 ins.i32_const(0);
256 ins.i32_store(memarg(0));
257 ins.local_get(0);
258 ins.local_get(0);
259 ins.i32_store(memarg(4));
260
261 // and return the local as it's the pointer to the ptr/len combo
262 ins.local_get(0);
263
264 ins.end();
265 self.accessor_code.function(&function);
266
267 let list_ty = self.component.types + self.extra_types.len();
268 self.extra_types
269 .defined_type()
270 .list(wasm_encoder::ComponentValType::Primitive(
271 wasm_encoder::PrimitiveValType::U8,
272 ));
273 let lift_type_index = self.component.types + self.extra_types.len();
274 self.extra_types
275 .function()
276 .params::<_, wasm_encoder::ComponentValType>([])
277 .result(Some(wasm_encoder::ComponentValType::Type(list_ty)));
278 self.extra_aliases
279 .alias(wasm_encoder::Alias::CoreInstanceExport {
280 instance: instance_index,
281 kind: wasm_encoder::ExportKind::Memory,
282 name: memory_export_name,
283 });
284 self.lift_accessor(
285 "memory",
286 lift_type_index,
287 &[wasm_encoder::CanonicalOption::Memory(
288 self.component.core_memories + self.accessor_nmemories,
289 )],
290 )
291 }
292
lift_accessor( &mut self, item: &str, lift_type_index: u32, opts: &[wasm_encoder::CanonicalOption], ) -> String293 fn lift_accessor(
294 &mut self,
295 item: &str,
296 lift_type_index: u32,
297 opts: &[wasm_encoder::CanonicalOption],
298 ) -> String {
299 let accessor_core_export_name = self.accessors.len().to_string();
300 self.accessor_exports.export(
301 &accessor_core_export_name,
302 wasm_encoder::ExportKind::Func,
303 self.accessor_functions.len() - 1,
304 );
305
306 self.extra_aliases
307 .alias(wasm_encoder::Alias::CoreInstanceExport {
308 instance: self.accessor_instance_index(),
309 kind: wasm_encoder::ExportKind::Func,
310 name: &accessor_core_export_name,
311 });
312 self.extra_core_funcs += 1;
313 self.extra_canonicals.lift(
314 self.component.core_funcs + self.extra_core_funcs - 1,
315 lift_type_index,
316 opts.iter().copied(),
317 );
318
319 let accessor_export_name = format!("{item}{}", self.accessors.len());
320 self.accessor_instance_export_items.push((
321 accessor_export_name.clone(),
322 wasm_encoder::ComponentExportKind::Func,
323 self.component.funcs + self.extra_canonicals.len() - 1,
324 ));
325 accessor_export_name
326 }
327
finish(&mut self, encoder: &mut wasm_encoder::Component)328 fn finish(&mut self, encoder: &mut wasm_encoder::Component) {
329 // Build the `accessor_module` and add it to the component.
330 let mut accessor_module = wasm_encoder::Module::new();
331 accessor_module.section(&self.accessor_types);
332 accessor_module.section(&self.accessor_imports);
333 accessor_module.section(&self.accessor_functions);
334 accessor_module.section(&self.accessor_exports);
335 accessor_module.section(&self.accessor_code);
336 encoder.section(&wasm_encoder::ModuleSection(&accessor_module));
337
338 // Instantiate the `accessor_module` with prior instantiations.
339 let mut extra_instances = wasm_encoder::InstanceSection::new();
340 extra_instances.instantiate(
341 self.component.num_core_modules(),
342 self.instances_to_instantiate_with
343 .iter()
344 .map(|(i, name)| (name.as_str(), wasm_encoder::ModuleArg::Instance(*i))),
345 );
346 encoder.section(&extra_instances);
347
348 // Add instrumentation to the component which extracts names from the
349 // accessor instance, lifts things into the component model, and then
350 // export them.
351 encoder.section(&self.extra_aliases);
352 encoder.section(&self.extra_types);
353 encoder.section(&self.extra_canonicals);
354 let mut extra_component_instances = wasm_encoder::ComponentInstanceSection::new();
355 extra_component_instances.export_items(
356 self.accessor_instance_export_items
357 .iter()
358 .map(|(a, b, c)| (a.as_str(), *b, *c)),
359 );
360 encoder.section(&extra_component_instances);
361
362 let mut extra_exports = wasm_encoder::ComponentExportSection::new();
363 extra_exports.export(
364 WIZER_INSTANCE,
365 wasm_encoder::ComponentExportKind::Instance,
366 self.component.instances,
367 None,
368 );
369 encoder.section(&extra_exports);
370 }
371
accessor_instance_index(&self) -> u32372 fn accessor_instance_index(&self) -> u32 {
373 self.component.core_instances
374 }
375 }
376