Skip to main content

gaia_assembler/backends/wasi/
mod.rs

1//! WASI (WebAssembly System Interface) backend compiler
2
3use super::{Backend, GeneratedFiles};
4use crate::{
5    config::GaiaConfig,
6    instruction::{CmpCondition, CoreInstruction, GaiaInstruction, ManagedInstruction},
7    program::{GaiaConstant, GaiaFunction, GaiaModule},
8    types::GaiaType,
9};
10use gaia_types::{
11    helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
12    Result,
13};
14use std::collections::HashMap;
15
16#[cfg(feature = "wasi-assembler")]
17use wasi_assembler::*;
18
19#[cfg(not(feature = "wasi-assembler"))]
20mod wasi_stub {
21    pub struct WasiProgram;
22    impl WasiProgram {
23        pub fn to_wasm(&self) -> gaia_types::Result<Vec<u8>> {
24            Err(gaia_types::errors::GaiaError::custom_error("WASI backend not enabled"))
25        }
26    }
27    #[allow(dead_code)]
28    pub enum WasmValueType {
29        I32,
30        I64,
31        F32,
32        F64,
33    }
34}
35#[cfg(not(feature = "wasi-assembler"))]
36use wasi_stub::*;
37
38/// WASI Backend implementation
39#[derive(Default)]
40pub struct WasiBackend {}
41
42impl Backend for WasiBackend {
43    fn name(&self) -> &'static str {
44        "WASI"
45    }
46
47    fn primary_target(&self) -> CompilationTarget {
48        CompilationTarget { build: Architecture::WASM32, host: AbiCompatible::WebAssemblyBinary, target: ApiCompatible::WASI }
49    }
50
51    fn artifact_type(&self) -> ArtifactType {
52        ArtifactType::Executable
53    }
54
55    fn match_score(&self, target: &CompilationTarget) -> f32 {
56        match target.host {
57            AbiCompatible::WebAssemblyBinary => 10.0,
58            AbiCompatible::WebAssemblyTextFormat => 5.0,
59            AbiCompatible::Unknown => match target.build {
60                // wat output, 5% support
61                Architecture::WASM32 => 5.0,
62                Architecture::WASM64 => 0.0,
63                _ => -100.0,
64            },
65            _ => -100.0,
66        }
67    }
68
69    fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
70        #[cfg(feature = "wasi-assembler")]
71        {
72            let mut context = create_wasi_context()?;
73            compile_program(&mut context, program)?;
74
75            let wasi_program = context.program;
76
77            let mut files = HashMap::new();
78
79            // 1. Generate .wasm
80            files.insert("main.wasm".to_string(), wasi_program.to_wasm()?);
81
82            Ok(GeneratedFiles { artifact_type: self.artifact_type(), files, custom: None, diagnostics: vec![] })
83        }
84        #[cfg(not(feature = "wasi-assembler"))]
85        {
86            let _ = program;
87            Err(gaia_types::errors::GaiaError::custom_error("WASI backend not enabled"))
88        }
89    }
90}
91
92impl WasiBackend {
93    /// Generate WASI WebAssembly bytecode from Gaia program
94    pub fn generate(program: &GaiaModule) -> Result<Vec<u8>> {
95        #[cfg(feature = "wasi-assembler")]
96        {
97            let mut context = create_wasi_context()?;
98            compile_program(&mut context, program)?;
99            context.program.to_wasm()
100        }
101        #[cfg(not(feature = "wasi-assembler"))]
102        {
103            let _ = program;
104            Err(gaia_types::errors::GaiaError::custom_error("WASI backend not enabled"))
105        }
106    }
107}
108
109/// Compile Gaia program to WASI WebAssembly
110pub fn compile(program: &GaiaModule) -> Result<Vec<u8>> {
111    WasiBackend::generate(program)
112}
113
114#[cfg(feature = "wasi-assembler")]
115struct WasiContext {
116    program: WasiProgram,
117    /// Import map
118    import_map: HashMap<String, u32>,
119}
120
121#[cfg(feature = "wasi-assembler")]
122impl WasiContext {
123    fn new() -> Self {
124        WasiContext { program: WasiProgram::new_core_module(), import_map: HashMap::new() }
125    }
126
127    fn add_import(&mut self, module: &str, field: &str, params: Vec<WasmValueType>, results: Vec<WasmValueType>) -> u32 {
128        let key = format!("{}.{}", module, field);
129        if let Some(&index) = self.import_map.get(&key) {
130            index
131        }
132        else {
133            let index = self.import_map.len() as u32;
134            let func_type = WasiFunctionType { params, results };
135            let type_index = self.program.add_function_type(func_type);
136
137            self.program.add_import(wasi_assembler::WasiImport {
138                module: module.to_string(),
139                field: field.to_string(),
140                import_type: wasi_assembler::WasmImportType::Function { type_index },
141            });
142
143            self.import_map.insert(key, index);
144            index
145        }
146    }
147
148    fn add_function(&mut self, body: Vec<WasiInstruction>, params: Vec<WasmValueType>, results: Vec<WasmValueType>) {
149        let func_type = WasiFunctionType { params, results };
150        let type_index = self.program.add_function_type(func_type);
151        let func = wasi_assembler::WasiFunction { type_index, locals: vec![], body };
152        self.program.add_function(func);
153    }
154}
155
156#[cfg(feature = "wasi-assembler")]
157fn create_wasi_context() -> Result<WasiContext> {
158    Ok(WasiContext::new())
159}
160
161#[cfg(feature = "wasi-assembler")]
162fn compile_program(context: &mut WasiContext, program: &GaiaModule) -> Result<()> {
163    // 2. Compile all functions
164    for function in &program.functions {
165        compile_function(context, function)?;
166    }
167
168    // 3. Export main function
169    if !context.program.functions.is_empty() {
170        let main_index = (context.program.imports.len() + context.program.functions.len() - 1) as u32;
171        context.program.add_export(wasi_assembler::WasiExport {
172            name: "main".to_string(),
173            export_type: wasi_assembler::WasmExportType::Function { function_index: main_index },
174        });
175    }
176
177    Ok(())
178}
179
180#[cfg(feature = "wasi-assembler")]
181fn compile_function(context: &mut WasiContext, function: &GaiaFunction) -> Result<()> {
182    let mut instrs = Vec::new();
183
184    // Compile code blocks
185    for block in &function.blocks {
186        for instruction in &block.instructions {
187            compile_instruction(context, &mut instrs, instruction)?;
188        }
189
190        // Compile terminators
191        match &block.terminator {
192            crate::program::GaiaTerminator::Jump(_label) => instrs.push(WasiInstruction::Br { label_index: 0 }), // FIXME
193            crate::program::GaiaTerminator::Branch { .. } => {
194                instrs.push(WasiInstruction::BrIf { label_index: 0 }); // FIXME
195            }
196            crate::program::GaiaTerminator::Return => instrs.push(WasiInstruction::Return),
197            crate::program::GaiaTerminator::Call { .. } => {
198                // TODO: Find function index
199                instrs.push(WasiInstruction::Call { function_index: 0 });
200            }
201            crate::program::GaiaTerminator::Halt => {
202                // WASI: exit(0)
203                instrs.push(WasiInstruction::I32Const { value: 0 });
204                // TODO: Find proc_exit index
205                instrs.push(WasiInstruction::Call { function_index: 0 });
206            }
207        }
208    }
209
210    instrs.push(WasiInstruction::End);
211
212    // Map parameters and return types
213    let params = function.signature.params.iter().map(|p| map_type(p)).collect();
214    let results = match function.signature.return_type {
215        GaiaType::Void => vec![],
216        _ => vec![map_type(&function.signature.return_type)],
217    };
218
219    // Add function to program
220    context.add_function(instrs, params, results);
221
222    Ok(())
223}
224
225#[cfg(feature = "wasi-assembler")]
226fn map_type(ty: &GaiaType) -> WasmValueType {
227    match ty {
228        GaiaType::I32 => WasmValueType::I32,
229        GaiaType::I64 => WasmValueType::I64,
230        GaiaType::F32 => WasmValueType::F32,
231        GaiaType::F64 => WasmValueType::F64,
232        _ => WasmValueType::I32, // Fallback
233    }
234}
235
236#[cfg(feature = "wasi-assembler")]
237fn compile_instruction(
238    _context: &mut WasiContext,
239    instrs: &mut Vec<WasiInstruction>,
240    instruction: &GaiaInstruction,
241) -> Result<()> {
242    match instruction {
243        GaiaInstruction::Core(core) => match core {
244            CoreInstruction::PushConstant(constant) => compile_load_constant(instrs, constant),
245            CoreInstruction::Load(gaia_type) => compile_load_indirect(instrs, gaia_type),
246            CoreInstruction::Store(gaia_type) => compile_store_indirect(instrs, gaia_type),
247            CoreInstruction::Add(_) => {
248                instrs.push(WasiInstruction::I32Add);
249                Ok(())
250            }
251            CoreInstruction::Sub(_) => {
252                instrs.push(WasiInstruction::I32Sub);
253                Ok(())
254            }
255            CoreInstruction::Mul(_) => {
256                instrs.push(WasiInstruction::I32Mul);
257                Ok(())
258            }
259            CoreInstruction::Div(_) => {
260                instrs.push(WasiInstruction::I32DivS);
261                Ok(())
262            }
263            CoreInstruction::Rem(_) => {
264                instrs.push(WasiInstruction::I32RemS);
265                Ok(())
266            }
267            CoreInstruction::And(_) => {
268                instrs.push(WasiInstruction::I32And);
269                Ok(())
270            }
271            CoreInstruction::Or(_) => {
272                instrs.push(WasiInstruction::I32Or);
273                Ok(())
274            }
275            CoreInstruction::Xor(_) => {
276                instrs.push(WasiInstruction::I32Xor);
277                Ok(())
278            }
279            CoreInstruction::Shl(_) => {
280                instrs.push(WasiInstruction::I32Shl);
281                Ok(())
282            }
283            CoreInstruction::Shr(_) => {
284                instrs.push(WasiInstruction::I32ShrS);
285                Ok(())
286            }
287            CoreInstruction::Pop => {
288                instrs.push(WasiInstruction::Drop);
289                Ok(())
290            }
291            CoreInstruction::LoadLocal(index, _) => {
292                instrs.push(WasiInstruction::LocalGet { local_index: *index });
293                Ok(())
294            }
295            CoreInstruction::StoreLocal(index, _) => {
296                instrs.push(WasiInstruction::LocalSet { local_index: *index });
297                Ok(())
298            }
299            CoreInstruction::LoadArg(index, _) => {
300                instrs.push(WasiInstruction::LocalGet { local_index: *index });
301                Ok(())
302            }
303            CoreInstruction::StoreArg(index, _) => {
304                instrs.push(WasiInstruction::LocalSet { local_index: *index });
305                Ok(())
306            }
307            CoreInstruction::Ret => {
308                instrs.push(WasiInstruction::Return);
309                Ok(())
310            }
311            CoreInstruction::Call(_, _) => {
312                // TODO: Find function index
313                instrs.push(WasiInstruction::Call { function_index: 0 });
314                Ok(())
315            }
316            CoreInstruction::Cmp(cond, _) => compile_compare(instrs, cond),
317            CoreInstruction::StructNew(_) => {
318                // TODO: Find type index
319                instrs.push(WasiInstruction::StructNew { type_index: 0 });
320                Ok(())
321            }
322            CoreInstruction::StructGet { field_index, .. } => {
323                // TODO: Find type index
324                instrs.push(WasiInstruction::StructGet { type_index: 0, field_index: *field_index as u32 });
325                Ok(())
326            }
327            CoreInstruction::StructSet { field_index, .. } => {
328                // TODO: Find type index
329                instrs.push(WasiInstruction::StructSet { type_index: 0, field_index: *field_index as u32 });
330                Ok(())
331            }
332            _ => Ok(()),
333        },
334        GaiaInstruction::Managed(managed) => match managed {
335            ManagedInstruction::CallStatic { .. } => {
336                // AOT mode maps static method calls to regular calls
337                instrs.push(WasiInstruction::Call { function_index: 0 });
338                Ok(())
339            }
340            _ => Ok(()),
341        },
342        _ => Ok(()),
343    }
344}
345
346#[cfg(feature = "wasi-assembler")]
347fn compile_compare(instrs: &mut Vec<WasiInstruction>, cond: &CmpCondition) -> Result<()> {
348    match cond {
349        CmpCondition::Eq => instrs.push(WasiInstruction::I32Eq),
350        CmpCondition::Ne => instrs.push(WasiInstruction::I32Ne),
351        CmpCondition::Lt => instrs.push(WasiInstruction::I32LtS),
352        CmpCondition::Le => instrs.push(WasiInstruction::I32LeS),
353        CmpCondition::Gt => instrs.push(WasiInstruction::I32GtS),
354        CmpCondition::Ge => instrs.push(WasiInstruction::I32GeS),
355    }
356    Ok(())
357}
358
359#[cfg(feature = "wasi-assembler")]
360fn compile_load_indirect(instrs: &mut Vec<WasiInstruction>, gaia_type: &GaiaType) -> Result<()> {
361    // Assume memory offset is already on top of the stack
362    match gaia_type {
363        GaiaType::I32 => instrs.push(WasiInstruction::I32Load { offset: 0, align: 0 }),
364        GaiaType::I64 => instrs.push(WasiInstruction::I64Load { offset: 0, align: 0 }),
365        GaiaType::F32 => instrs.push(WasiInstruction::F32Load { offset: 0, align: 0 }),
366        GaiaType::F64 => instrs.push(WasiInstruction::F64Load { offset: 0, align: 0 }),
367        _ => {}
368    }
369    Ok(())
370}
371
372#[cfg(feature = "wasi-assembler")]
373fn compile_store_indirect(instrs: &mut Vec<WasiInstruction>, gaia_type: &GaiaType) -> Result<()> {
374    // Assume [ptr, value] are already on the stack
375    match gaia_type {
376        GaiaType::I32 => instrs.push(WasiInstruction::I32Store { offset: 0, align: 0 }),
377        GaiaType::I64 => instrs.push(WasiInstruction::I64Store { offset: 0, align: 0 }),
378        GaiaType::F32 => instrs.push(WasiInstruction::F32Store { offset: 0, align: 0 }),
379        GaiaType::F64 => instrs.push(WasiInstruction::F64Store { offset: 0, align: 0 }),
380        _ => {}
381    }
382    Ok(())
383}
384
385#[cfg(feature = "wasi-assembler")]
386fn compile_load_constant(instrs: &mut Vec<WasiInstruction>, constant: &GaiaConstant) -> Result<()> {
387    match constant {
388        GaiaConstant::I32(value) => instrs.push(WasiInstruction::I32Const { value: *value }),
389        GaiaConstant::I64(value) => instrs.push(WasiInstruction::I64Const { value: *value }),
390        GaiaConstant::F32(value) => instrs.push(WasiInstruction::F32Const { value: *value }),
391        GaiaConstant::F64(value) => instrs.push(WasiInstruction::F64Const { value: *value }),
392        _ => {}
393    }
394    Ok(())
395}