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        let writer = clr_assembler::formats::dll::writer::DllWriter::new(buffer);
34        let result = writer.write(&clr_program);
35        if let Some(error) = result.diagnostics.into_iter().next() {
36            return Err(error);
37        }
38        Ok(result.result.expect("Failed to get DLL buffer").into_inner())
39    }
40}
41
42#[cfg(feature = "clr-assembler")]
43impl Backend for ClrBackend {
44    fn name(&self) -> &'static str {
45        "MSIL"
46    }
47
48    fn primary_target(&self) -> CompilationTarget {
49        CompilationTarget {
50            build: Architecture::CLR,
51            host: AbiCompatible::MicrosoftIntermediateLanguage,
52            target: ApiCompatible::ClrRuntime(4),
53        }
54    }
55
56    fn artifact_type(&self) -> ArtifactType {
57        ArtifactType::Bytecode
58    }
59
60    fn match_score(&self, target: &CompilationTarget) -> f32 {
61        match target.build {
62            Architecture::CLR => match target.host {
63                AbiCompatible::Unknown => 30.0,
64                AbiCompatible::MicrosoftIntermediateLanguage => 30.0,
65                _ => -100.0,
66            },
67            _ => -100.0,
68        }
69    }
70
71    fn generate(&self, program: &GaiaModule, _config: &GaiaConfig) -> Result<GeneratedFiles> {
72        let clr_program = convert_to_clr_program(program, Some(&_config.setting))?;
73        let mut files = HashMap::new();
74
75        match _config.target.host {
76            AbiCompatible::Unknown => {
77                let buffer = std::io::Cursor::new(Vec::new());
78                let writer = clr_assembler::formats::dll::writer::DllWriter::new(buffer);
79                let result = writer.write(&clr_program);
80                if let Some(error) = result.diagnostics.into_iter().next() {
81                    return Err(error);
82                }
83                files.insert("main.dll".to_string(), result.result.expect("Failed to get DLL buffer").into_inner());
84            }
85            AbiCompatible::MicrosoftIntermediateLanguage => {
86                let source = clr_assembler::formats::msil::program_to_source(&clr_program);
87                files.insert("main.il".to_string(), source.into_bytes());
88            }
89            _ => Err(GaiaError::invalid_data("Unsupported host ABI for CLR backend"))?,
90        }
91
92        Ok(GeneratedFiles { artifact_type: ArtifactType::Bytecode, files, custom: None, diagnostics: vec![] })
93    }
94}
95
96#[cfg(feature = "clr-assembler")]
97struct IlContext {
98    builder: ClrBuilder,
99    function_mapper: FunctionMapper,
100    #[allow(dead_code)]
101    field_types: HashMap<(String, String), GaiaType>,
102}
103
104#[cfg(feature = "clr-assembler")]
105impl IlContext {
106    fn new(name: String) -> Self {
107        Self { builder: ClrBuilder::new(name), function_mapper: FunctionMapper::new(), field_types: HashMap::new() }
108    }
109
110    fn map_function(&self, raw_name: &str) -> String {
111        let il_target = CompilationTarget {
112            build: Architecture::CLR,
113            host: AbiCompatible::MicrosoftIntermediateLanguage,
114            target: ApiCompatible::ClrRuntime(4),
115        };
116        self.function_mapper.map_function(&il_target, raw_name).unwrap_or(raw_name).to_string()
117    }
118}
119
120#[cfg(feature = "clr-assembler")]
121fn convert_to_clr_program(program: &GaiaModule, settings: Option<&GaiaSettings>) -> Result<ClrProgram> {
122    let mut context = IlContext::new(program.name.clone());
123    if let Some(s) = settings {
124        context.function_mapper = FunctionMapper::from_config(s).unwrap_or_default();
125    }
126
127    // Handle imports
128    for import in &program.imports {
129        context.builder.add_external_assembly(import.library.clone());
130    }
131
132    // Compile all structs
133    for s in &program.structs {
134        context.builder.begin_class(s.name.clone(), None);
135        for (name, ty) in &s.fields {
136            // TODO: Add fields to builder if needed, for now we just handle methods
137            context.field_types.insert((s.name.clone(), name.clone()), ty.clone());
138        }
139        // ClrBuilder will need more methods to handle fields if they are to be included in ClrProgram
140    }
141
142    // Compile all functions
143    for function in &program.functions {
144        let _is_entry = function.name == "main";
145        let ret_type = gaia_type_to_clr_type(&function.signature.return_type);
146
147        context.builder.begin_method(function.name.clone(), ret_type);
148        // Note: Set entry point flag if needed. ClrBuilder needs to support this.
149
150        for block in &function.blocks {
151            for instruction in &block.instructions {
152                compile_instruction(&mut context, instruction)?;
153            }
154
155            match &block.terminator {
156                crate::program::GaiaTerminator::Return => {
157                    context.builder.emit(ClrOpcode::Ret, None);
158                }
159                crate::program::GaiaTerminator::Call { callee, .. } => {
160                    let mapped = context.map_function(callee);
161                    context.builder.emit(
162                        ClrOpcode::Call,
163                        Some(ClrInstructionOperand::Method(
164                            mapped,
165                            None,
166                            vec![],
167                            Box::new(ClrTypeReference::Primitive("void".to_string())),
168                        )),
169                    );
170                }
171                _ => {} // Handle other terminators
172            }
173        }
174    }
175
176    Ok(context.builder.finish())
177}
178
179#[cfg(feature = "clr-assembler")]
180fn compile_instruction(context: &mut IlContext, instruction: &GaiaInstruction) -> Result<()> {
181    match instruction {
182        GaiaInstruction::Core(core) => match core {
183            CoreInstruction::PushConstant(constant) => match constant {
184                GaiaConstant::I32(v) => context.builder.emit(ClrOpcode::LdcI4, Some(ClrInstructionOperand::Int32(*v))),
185                GaiaConstant::String(s) => {
186                    context.builder.emit(ClrOpcode::Ldstr, Some(ClrInstructionOperand::String(s.clone())))
187                }
188                _ => {}
189            },
190            CoreInstruction::Add(_) => context.builder.emit(ClrOpcode::Add, None),
191            CoreInstruction::Sub(_) => context.builder.emit(ClrOpcode::Sub, None),
192            CoreInstruction::Mul(_) => context.builder.emit(ClrOpcode::Mul, None),
193            CoreInstruction::Div(_) => context.builder.emit(ClrOpcode::Div, None),
194            _ => {}
195        },
196        _ => {}
197    }
198    Ok(())
199}
200
201#[cfg(feature = "clr-assembler")]
202fn gaia_type_to_clr_type(t: &GaiaType) -> ClrTypeReference {
203    match t {
204        GaiaType::I32 => ClrTypeReference::Primitive("int32".to_string()),
205        GaiaType::String => ClrTypeReference::Primitive("string".to_string()),
206        GaiaType::Void => ClrTypeReference::Primitive("void".to_string()),
207        _ => ClrTypeReference::Primitive("object".to_string()),
208    }
209}