gaia_assembler/backends/wasi/
mod.rs1use 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#[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 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 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 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
109pub 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: 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 for function in &program.functions {
165 compile_function(context, function)?;
166 }
167
168 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 for block in &function.blocks {
186 for instruction in &block.instructions {
187 compile_instruction(context, &mut instrs, instruction)?;
188 }
189
190 match &block.terminator {
192 crate::program::GaiaTerminator::Jump(_label) => instrs.push(WasiInstruction::Br { label_index: 0 }), crate::program::GaiaTerminator::Branch { .. } => {
194 instrs.push(WasiInstruction::BrIf { label_index: 0 }); }
196 crate::program::GaiaTerminator::Return => instrs.push(WasiInstruction::Return),
197 crate::program::GaiaTerminator::Call { .. } => {
198 instrs.push(WasiInstruction::Call { function_index: 0 });
200 }
201 crate::program::GaiaTerminator::Halt => {
202 instrs.push(WasiInstruction::I32Const { value: 0 });
204 instrs.push(WasiInstruction::Call { function_index: 0 });
206 }
207 }
208 }
209
210 instrs.push(WasiInstruction::End);
211
212 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 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, }
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 instrs.push(WasiInstruction::Call { function_index: 0 });
314 Ok(())
315 }
316 CoreInstruction::Cmp(cond, _) => compile_compare(instrs, cond),
317 CoreInstruction::StructNew(_) => {
318 instrs.push(WasiInstruction::StructNew { type_index: 0 });
320 Ok(())
321 }
322 CoreInstruction::StructGet { field_index, .. } => {
323 instrs.push(WasiInstruction::StructGet { type_index: 0, field_index: *field_index as u32 });
325 Ok(())
326 }
327 CoreInstruction::StructSet { field_index, .. } => {
328 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 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 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 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}