Skip to main content

gaia_assembler/backends/dotnet/
msil.rs

1//! IL (Intermediate Language) backend compiler
2
3#[cfg(feature = "clr-assembler")]
4use crate::{
5    adapters::FunctionMapper,
6    config::{GaiaConfig, GaiaSettings},
7    instruction::{CmpCondition, CoreInstruction, GaiaInstruction},
8    program::{GaiaConstant, GaiaFunction, GaiaModule},
9    types::GaiaType,
10    Backend, GeneratedFiles,
11};
12#[cfg(feature = "clr-assembler")]
13use clr_assembler::program::*;
14#[cfg(feature = "clr-assembler")]
15use gaia_types::{
16    helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
17    GaiaError, Result,
18};
19#[cfg(feature = "clr-assembler")]
20use std::collections::HashMap;
21
22/// IL Backend implementation
23#[derive(Default)]
24#[cfg(feature = "clr-assembler")]
25pub struct ClrBackend {}
26
27#[cfg(feature = "clr-assembler")]
28impl ClrBackend {
29    /// Generate CLR assembly bytecode with specified settings
30    pub fn generate_with_settings(program: &GaiaModule, settings: &GaiaSettings) -> Result<Vec<u8>> {
31        let clr_program = convert_to_clr_program(program, Some(settings))?;
32        let buffer = std::io::Cursor::new(Vec::new());
33        
34        // Check if main function exists to determine whether to generate EXE or DLL
35        let has_main = program.functions.iter().any(|f| f.name == "main");
36        
37        let result = if has_main {
38            // Generate EXE file
39            let writer = clr_assembler::formats::exe::writer::DotNetWriter::new(buffer);
40            writer.write(&clr_program)
41        } else {
42            // Generate DLL file
43            let writer = clr_assembler::formats::dll::writer::DllWriter::new(buffer);
44            writer.write(&clr_program)
45        };
46        
47        if let Some(error) = result.diagnostics.into_iter().next() {
48            return Err(error);
49        }
50        
51        Ok(result.result.expect("Failed to get buffer").into_inner())
52    }
53}
54
55#[cfg(feature = "clr-assembler")]
56impl Backend for ClrBackend {
57    fn name(&self) -> &'static str {
58        "MSIL"
59    }
60
61    fn primary_target(&self) -> CompilationTarget {
62        CompilationTarget {
63            build: Architecture::CLR,
64            host: AbiCompatible::MicrosoftIntermediateLanguage,
65            target: ApiCompatible::ClrRuntime(4),
66        }
67    }
68
69    fn artifact_type(&self) -> ArtifactType {
70        ArtifactType::Bytecode
71    }
72
73    fn match_score(&self, target: &CompilationTarget) -> f32 {
74        match target.build {
75            Architecture::CLR => match target.host {
76                AbiCompatible::Unknown => 30.0,
77                AbiCompatible::MicrosoftIntermediateLanguage => 30.0,
78                _ => -100.0,
79            },
80            _ => -100.0,
81        }
82    }
83
84    fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
85        let clr_program = convert_to_clr_program(program, Some(&config.setting))?;
86        let mut files = HashMap::new();
87
88        match config.target.host {
89            AbiCompatible::Unknown => {
90                let buffer = std::io::Cursor::new(Vec::new());
91                let writer = clr_assembler::formats::dll::writer::DllWriter::new(buffer);
92                let result = writer.write(&clr_program);
93                if let Some(error) = result.diagnostics.into_iter().next() {
94                    return Err(error);
95                }
96                files.insert("main.dll".to_string(), result.result.expect("Failed to get DLL buffer").into_inner());
97            }
98            AbiCompatible::MicrosoftIntermediateLanguage => {
99                let source = clr_assembler::formats::msil::program_to_source(&clr_program);
100                files.insert("main.il".to_string(), source.into_bytes());
101            }
102            _ => Err(GaiaError::invalid_data("Unsupported host ABI for CLR backend"))?,
103        }
104
105        Ok(GeneratedFiles { artifact_type: ArtifactType::Bytecode, files, custom: None, diagnostics: vec![] })
106    }
107}
108
109#[cfg(feature = "clr-assembler")]
110struct IlContext {
111    builder: ClrBuilder,
112    function_mapper: FunctionMapper,
113    #[allow(dead_code)]
114    field_types: HashMap<(String, String), GaiaType>,
115}
116
117#[cfg(feature = "clr-assembler")]
118impl IlContext {
119    fn new(name: String) -> Self {
120        Self { builder: ClrBuilder::new(name), function_mapper: FunctionMapper::new(), field_types: HashMap::new() }
121    }
122
123    fn map_function(&self, raw_name: &str) -> String {
124        let il_target = CompilationTarget {
125            build: Architecture::CLR,
126            host: AbiCompatible::MicrosoftIntermediateLanguage,
127            target: ApiCompatible::ClrRuntime(4),
128        };
129        self.function_mapper.map_function(&il_target, raw_name).unwrap_or(raw_name).to_string()
130    }
131}
132
133#[cfg(feature = "clr-assembler")]
134fn convert_to_clr_program(program: &GaiaModule, settings: Option<&GaiaSettings>) -> Result<ClrProgram> {
135    let mut context = IlContext::new(program.name.clone());
136    if let Some(s) = settings {
137        context.function_mapper = FunctionMapper::from_config(s).unwrap_or_default();
138    }
139
140    // Handle imports
141    for import in &program.imports {
142        context.builder.add_external_assembly(import.library.clone());
143    }
144
145    // Create a default class for global functions
146    let default_class_name = if program.name.is_empty() { "Program" } else { &program.name };
147    context.builder.begin_class(default_class_name.to_string(), None);
148
149    // Compile all classes
150    for class in &program.classes {
151        context.builder.begin_class(class.name.clone(), class.parent.clone());
152        for method in &class.methods {
153            let ret_type = gaia_type_to_clr_type(&method.signature.return_type);
154            
155            context.builder.begin_method(method.name.clone(), ret_type);
156            
157            for block in &method.blocks {
158                for instruction in &block.instructions {
159                    compile_instruction(&mut context, instruction)?;
160                }
161
162                match &block.terminator {
163                    crate::program::GaiaTerminator::Return => {
164                        context.builder.emit(ClrOpcode::Ret, None);
165                    }
166                    _ => {} // Handle other terminators
167                }
168            }
169        }
170    }
171
172    // Compile all functions
173    for function in &program.functions {
174        let ret_type = gaia_type_to_clr_type(&function.signature.return_type);
175
176        context.builder.begin_method(function.name.clone(), ret_type);
177
178        for block in &function.blocks {
179            for instruction in &block.instructions {
180                compile_instruction(&mut context, instruction)?;
181            }
182
183            match &block.terminator {
184                crate::program::GaiaTerminator::Return => {
185                    context.builder.emit(ClrOpcode::Ret, None);
186                }
187                crate::program::GaiaTerminator::Call { callee, .. } => {
188                    let mapped = context.map_function(callee);
189                    context.builder.emit(
190                        ClrOpcode::Call,
191                        Some(ClrInstructionOperand::Method(
192                            mapped,
193                            None,
194                            vec![],
195                            Box::new(ClrTypeReference::Primitive("void".to_string())),
196                        )),
197                    );
198                }
199                _ => {} // Handle other terminators
200            }
201        }
202    }
203
204    Ok(context.builder.finish())
205}
206
207#[cfg(feature = "clr-assembler")]
208fn compile_instruction(context: &mut IlContext, instruction: &GaiaInstruction) -> Result<()> {
209    match instruction {
210        GaiaInstruction::Core(core) => match core {
211            CoreInstruction::PushConstant(constant) => match constant {
212                GaiaConstant::I32(v) => context.builder.emit(ClrOpcode::LdcI4, Some(ClrInstructionOperand::Int32(*v))),
213                GaiaConstant::I64(v) => context.builder.emit(ClrOpcode::LdcI8, Some(ClrInstructionOperand::Int64(*v))),
214                GaiaConstant::F32(v) => context.builder.emit(ClrOpcode::LdcR4, Some(ClrInstructionOperand::Float32(*v))),
215                GaiaConstant::F64(v) => context.builder.emit(ClrOpcode::LdcR8, Some(ClrInstructionOperand::Float64(*v))),
216                GaiaConstant::Bool(v) => context.builder.emit(ClrOpcode::LdcI4, Some(ClrInstructionOperand::Int32(if *v { 1 } else { 0 }))),
217                GaiaConstant::String(s) => {
218                    context.builder.emit(ClrOpcode::Ldstr, Some(ClrInstructionOperand::String(s.clone())))
219                }
220                _ => {}
221            },
222            CoreInstruction::Add(_) => context.builder.emit(ClrOpcode::Add, None),
223            CoreInstruction::Sub(_) => context.builder.emit(ClrOpcode::Sub, None),
224            CoreInstruction::Mul(_) => context.builder.emit(ClrOpcode::Mul, None),
225            CoreInstruction::Div(_) => context.builder.emit(ClrOpcode::Div, None),
226            CoreInstruction::Rem(_) => context.builder.emit(ClrOpcode::Rem, None),
227            CoreInstruction::And(_) => context.builder.emit(ClrOpcode::And, None),
228            CoreInstruction::Or(_) => context.builder.emit(ClrOpcode::Or, None),
229            CoreInstruction::Xor(_) => context.builder.emit(ClrOpcode::Xor, None),
230            CoreInstruction::Shl(_) => context.builder.emit(ClrOpcode::Shl, None),
231            CoreInstruction::Shr(_) => context.builder.emit(ClrOpcode::Shr, None),
232            CoreInstruction::Not(_) => context.builder.emit(ClrOpcode::Not, None),
233            CoreInstruction::Neg(_) => context.builder.emit(ClrOpcode::Neg, None),
234            CoreInstruction::Cmp(_, _) => context.builder.emit(ClrOpcode::Ceq, None),
235            CoreInstruction::Call(name, _) => {
236                let mapped = context.map_function(name);
237                context.builder.emit(
238                    ClrOpcode::Call,
239                    Some(ClrInstructionOperand::Method(
240                        mapped,
241                        None,
242                        vec![],
243                        Box::new(ClrTypeReference::Primitive("object".to_string())),
244                    )),
245                );
246            },
247            CoreInstruction::LoadLocal(idx, _) => {
248                context.builder.emit(ClrOpcode::Ldloc, Some(ClrInstructionOperand::Int32(*idx as i32)));
249            },
250            CoreInstruction::StoreLocal(idx, _) => {
251                context.builder.emit(ClrOpcode::Stloc, Some(ClrInstructionOperand::Int32(*idx as i32)));
252            },
253            CoreInstruction::LoadArg(idx, _) => {
254                context.builder.emit(ClrOpcode::Ldarg, Some(ClrInstructionOperand::Int32(*idx as i32)));
255            },
256            CoreInstruction::StoreArg(idx, _) => {
257                context.builder.emit(ClrOpcode::Starg, Some(ClrInstructionOperand::Int32(*idx as i32)));
258            },
259            CoreInstruction::Br(label) => {
260                context.builder.emit(ClrOpcode::Br, Some(ClrInstructionOperand::Branch(0)));
261            },
262            CoreInstruction::BrTrue(label) => {
263                context.builder.emit(ClrOpcode::Brtrue, Some(ClrInstructionOperand::Branch(0)));
264            },
265            CoreInstruction::BrFalse(label) => {
266                context.builder.emit(ClrOpcode::Brfalse, Some(ClrInstructionOperand::Branch(0)));
267            },
268            CoreInstruction::Label(label) => {
269                context.builder.emit(ClrOpcode::Nop, None);
270            },
271            CoreInstruction::Throw => {
272                context.builder.emit(ClrOpcode::Throw, None);
273            },
274            _ => {}
275        },
276        GaiaInstruction::Managed(managed) => match managed {
277            crate::instruction::ManagedInstruction::CallMethod { target, method, signature, is_virtual, .. } => {
278                let ret_type = gaia_type_to_clr_type(&signature.return_type);
279                let param_types: Vec<ClrTypeReference> = signature.params.iter().map(gaia_type_to_clr_type).collect();
280                context.builder.emit(
281                    if *is_virtual {
282                        ClrOpcode::Callvirt
283                    } else {
284                        ClrOpcode::Call
285                    },
286                    Some(ClrInstructionOperand::Method(
287                        method.clone(),
288                        Some(target.clone()),
289                        param_types,
290                        Box::new(ret_type),
291                    )),
292                );
293            },
294            crate::instruction::ManagedInstruction::CallStatic { target, method, signature, .. } => {
295                let ret_type = gaia_type_to_clr_type(&signature.return_type);
296                let param_types: Vec<ClrTypeReference> = signature.params.iter().map(gaia_type_to_clr_type).collect();
297                context.builder.emit(
298                    ClrOpcode::Call,
299                    Some(ClrInstructionOperand::Method(
300                        method.clone(),
301                        Some(target.clone()),
302                        param_types,
303                        Box::new(ret_type),
304                    )),
305                );
306            },
307            _ => {}
308        },
309        _ => {}
310    }
311    Ok(())
312}
313
314#[cfg(feature = "clr-assembler")]
315fn gaia_type_to_clr_type(t: &GaiaType) -> ClrTypeReference {
316    match t {
317        GaiaType::I8 => ClrTypeReference::Primitive("int8".to_string()),
318        GaiaType::I16 => ClrTypeReference::Primitive("int16".to_string()),
319        GaiaType::I32 => ClrTypeReference::Primitive("int32".to_string()),
320        GaiaType::I64 => ClrTypeReference::Primitive("int64".to_string()),
321        GaiaType::U8 => ClrTypeReference::Primitive("uint8".to_string()),
322        GaiaType::U16 => ClrTypeReference::Primitive("uint16".to_string()),
323        GaiaType::U32 => ClrTypeReference::Primitive("uint32".to_string()),
324        GaiaType::U64 => ClrTypeReference::Primitive("uint64".to_string()),
325        GaiaType::F32 => ClrTypeReference::Primitive("float32".to_string()),
326        GaiaType::F64 => ClrTypeReference::Primitive("float64".to_string()),
327        GaiaType::Bool => ClrTypeReference::Primitive("bool".to_string()),
328        GaiaType::String => ClrTypeReference::Primitive("string".to_string()),
329        GaiaType::Void => ClrTypeReference::Primitive("void".to_string()),
330        GaiaType::Struct(name) => ClrTypeReference::Type(name.clone(), None),
331        GaiaType::Class(name) => ClrTypeReference::Type(name.clone(), None),
332        GaiaType::Array(inner, _) => ClrTypeReference::Array(Box::new(gaia_type_to_clr_type(inner))),
333        _ => ClrTypeReference::Primitive("object".to_string()),
334    }
335}