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