gaia-assembler 0.1.1

Universal assembler framework for Gaia project
Documentation
//! IL (Intermediate Language) backend compiler

#[cfg(feature = "clr-assembler")]
use crate::{
    adapters::FunctionMapper,
    config::{GaiaConfig, GaiaSettings},
    instruction::{CmpCondition, CoreInstruction, GaiaInstruction},
    program::{GaiaConstant, GaiaFunction, GaiaModule},
    types::GaiaType,
    Backend, GeneratedFiles,
};
#[cfg(feature = "clr-assembler")]
use clr_assembler::program::*;
#[cfg(feature = "clr-assembler")]
use gaia_types::{
    helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
    GaiaError, Result,
};
#[cfg(feature = "clr-assembler")]
use std::collections::HashMap;

/// IL Backend implementation
#[derive(Default)]
#[cfg(feature = "clr-assembler")]
pub struct ClrBackend {}

#[cfg(feature = "clr-assembler")]
impl ClrBackend {
    /// Generate CLR assembly bytecode with specified settings
    pub fn generate_with_settings(program: &GaiaModule, settings: &GaiaSettings) -> Result<Vec<u8>> {
        let clr_program = convert_to_clr_program(program, Some(settings))?;
        let buffer = std::io::Cursor::new(Vec::new());
        
        // Check if main function exists to determine whether to generate EXE or DLL
        let has_main = program.functions.iter().any(|f| f.name == "main");
        
        let result = if has_main {
            // Generate EXE file
            let writer = clr_assembler::formats::exe::writer::DotNetWriter::new(buffer);
            writer.write(&clr_program)
        } else {
            // Generate DLL file
            let writer = clr_assembler::formats::dll::writer::DllWriter::new(buffer);
            writer.write(&clr_program)
        };
        
        if let Some(error) = result.diagnostics.into_iter().next() {
            return Err(error);
        }
        
        Ok(result.result.expect("Failed to get buffer").into_inner())
    }
}

#[cfg(feature = "clr-assembler")]
impl Backend for ClrBackend {
    fn name(&self) -> &'static str {
        "MSIL"
    }

    fn primary_target(&self) -> CompilationTarget {
        CompilationTarget {
            build: Architecture::CLR,
            host: AbiCompatible::MicrosoftIntermediateLanguage,
            target: ApiCompatible::ClrRuntime(4),
        }
    }

    fn artifact_type(&self) -> ArtifactType {
        ArtifactType::Bytecode
    }

    fn match_score(&self, target: &CompilationTarget) -> f32 {
        match target.build {
            Architecture::CLR => match target.host {
                AbiCompatible::Unknown => 30.0,
                AbiCompatible::MicrosoftIntermediateLanguage => 30.0,
                _ => -100.0,
            },
            _ => -100.0,
        }
    }

    fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
        let clr_program = convert_to_clr_program(program, Some(&config.setting))?;
        let mut files = HashMap::new();

        match config.target.host {
            AbiCompatible::Unknown => {
                let buffer = std::io::Cursor::new(Vec::new());
                let writer = clr_assembler::formats::dll::writer::DllWriter::new(buffer);
                let result = writer.write(&clr_program);
                if let Some(error) = result.diagnostics.into_iter().next() {
                    return Err(error);
                }
                files.insert("main.dll".to_string(), result.result.expect("Failed to get DLL buffer").into_inner());
            }
            AbiCompatible::MicrosoftIntermediateLanguage => {
                let source = clr_assembler::formats::msil::program_to_source(&clr_program);
                files.insert("main.il".to_string(), source.into_bytes());
            }
            _ => Err(GaiaError::invalid_data("Unsupported host ABI for CLR backend"))?,
        }

        Ok(GeneratedFiles { artifact_type: ArtifactType::Bytecode, files, custom: None, diagnostics: vec![] })
    }
}

#[cfg(feature = "clr-assembler")]
struct IlContext {
    builder: ClrBuilder,
    function_mapper: FunctionMapper,
    #[allow(dead_code)]
    field_types: HashMap<(String, String), GaiaType>,
}

#[cfg(feature = "clr-assembler")]
impl IlContext {
    fn new(name: String) -> Self {
        Self { builder: ClrBuilder::new(name), function_mapper: FunctionMapper::new(), field_types: HashMap::new() }
    }

    fn map_function(&self, raw_name: &str) -> String {
        let il_target = CompilationTarget {
            build: Architecture::CLR,
            host: AbiCompatible::MicrosoftIntermediateLanguage,
            target: ApiCompatible::ClrRuntime(4),
        };
        self.function_mapper.map_function(&il_target, raw_name).unwrap_or(raw_name).to_string()
    }
}

#[cfg(feature = "clr-assembler")]
fn convert_to_clr_program(program: &GaiaModule, settings: Option<&GaiaSettings>) -> Result<ClrProgram> {
    let mut context = IlContext::new(program.name.clone());
    if let Some(s) = settings {
        context.function_mapper = FunctionMapper::from_config(s).unwrap_or_default();
    }

    // Handle imports
    for import in &program.imports {
        context.builder.add_external_assembly(import.library.clone());
    }

    // Create a default class for global functions
    let default_class_name = if program.name.is_empty() { "Program" } else { &program.name };
    context.builder.begin_class(default_class_name.to_string(), None);

    // Compile all classes
    for class in &program.classes {
        context.builder.begin_class(class.name.clone(), class.parent.clone());
        for method in &class.methods {
            let ret_type = gaia_type_to_clr_type(&method.signature.return_type);
            
            context.builder.begin_method(method.name.clone(), ret_type);
            
            for block in &method.blocks {
                for instruction in &block.instructions {
                    compile_instruction(&mut context, instruction)?;
                }

                match &block.terminator {
                    crate::program::GaiaTerminator::Return => {
                        context.builder.emit(ClrOpcode::Ret, None);
                    }
                    _ => {} // Handle other terminators
                }
            }
        }
    }

    // Compile all functions
    for function in &program.functions {
        let ret_type = gaia_type_to_clr_type(&function.signature.return_type);

        context.builder.begin_method(function.name.clone(), ret_type);

        for block in &function.blocks {
            for instruction in &block.instructions {
                compile_instruction(&mut context, instruction)?;
            }

            match &block.terminator {
                crate::program::GaiaTerminator::Return => {
                    context.builder.emit(ClrOpcode::Ret, None);
                }
                crate::program::GaiaTerminator::Call { callee, .. } => {
                    let mapped = context.map_function(callee);
                    context.builder.emit(
                        ClrOpcode::Call,
                        Some(ClrInstructionOperand::Method(
                            mapped,
                            None,
                            vec![],
                            Box::new(ClrTypeReference::Primitive("void".to_string())),
                        )),
                    );
                }
                _ => {} // Handle other terminators
            }
        }
    }

    Ok(context.builder.finish())
}

#[cfg(feature = "clr-assembler")]
fn compile_instruction(context: &mut IlContext, instruction: &GaiaInstruction) -> Result<()> {
    match instruction {
        GaiaInstruction::Core(core) => match core {
            CoreInstruction::PushConstant(constant) => match constant {
                GaiaConstant::I32(v) => context.builder.emit(ClrOpcode::LdcI4, Some(ClrInstructionOperand::Int32(*v))),
                GaiaConstant::I64(v) => context.builder.emit(ClrOpcode::LdcI8, Some(ClrInstructionOperand::Int64(*v))),
                GaiaConstant::F32(v) => context.builder.emit(ClrOpcode::LdcR4, Some(ClrInstructionOperand::Float32(*v))),
                GaiaConstant::F64(v) => context.builder.emit(ClrOpcode::LdcR8, Some(ClrInstructionOperand::Float64(*v))),
                GaiaConstant::Bool(v) => context.builder.emit(ClrOpcode::LdcI4, Some(ClrInstructionOperand::Int32(if *v { 1 } else { 0 }))),
                GaiaConstant::String(s) => {
                    context.builder.emit(ClrOpcode::Ldstr, Some(ClrInstructionOperand::String(s.clone())))
                }
                _ => {}
            },
            CoreInstruction::Add(_) => context.builder.emit(ClrOpcode::Add, None),
            CoreInstruction::Sub(_) => context.builder.emit(ClrOpcode::Sub, None),
            CoreInstruction::Mul(_) => context.builder.emit(ClrOpcode::Mul, None),
            CoreInstruction::Div(_) => context.builder.emit(ClrOpcode::Div, None),
            CoreInstruction::Rem(_) => context.builder.emit(ClrOpcode::Rem, None),
            CoreInstruction::And(_) => context.builder.emit(ClrOpcode::And, None),
            CoreInstruction::Or(_) => context.builder.emit(ClrOpcode::Or, None),
            CoreInstruction::Xor(_) => context.builder.emit(ClrOpcode::Xor, None),
            CoreInstruction::Shl(_) => context.builder.emit(ClrOpcode::Shl, None),
            CoreInstruction::Shr(_) => context.builder.emit(ClrOpcode::Shr, None),
            CoreInstruction::Not(_) => context.builder.emit(ClrOpcode::Not, None),
            CoreInstruction::Neg(_) => context.builder.emit(ClrOpcode::Neg, None),
            CoreInstruction::Cmp(_, _) => context.builder.emit(ClrOpcode::Ceq, None),
            CoreInstruction::Call(name, _) => {
                let mapped = context.map_function(name);
                context.builder.emit(
                    ClrOpcode::Call,
                    Some(ClrInstructionOperand::Method(
                        mapped,
                        None,
                        vec![],
                        Box::new(ClrTypeReference::Primitive("object".to_string())),
                    )),
                );
            },
            CoreInstruction::LoadLocal(idx, _) => {
                context.builder.emit(ClrOpcode::Ldloc, Some(ClrInstructionOperand::Int32(*idx as i32)));
            },
            CoreInstruction::StoreLocal(idx, _) => {
                context.builder.emit(ClrOpcode::Stloc, Some(ClrInstructionOperand::Int32(*idx as i32)));
            },
            CoreInstruction::LoadArg(idx, _) => {
                context.builder.emit(ClrOpcode::Ldarg, Some(ClrInstructionOperand::Int32(*idx as i32)));
            },
            CoreInstruction::StoreArg(idx, _) => {
                context.builder.emit(ClrOpcode::Starg, Some(ClrInstructionOperand::Int32(*idx as i32)));
            },
            CoreInstruction::Br(label) => {
                context.builder.emit(ClrOpcode::Br, Some(ClrInstructionOperand::Branch(0)));
            },
            CoreInstruction::BrTrue(label) => {
                context.builder.emit(ClrOpcode::Brtrue, Some(ClrInstructionOperand::Branch(0)));
            },
            CoreInstruction::BrFalse(label) => {
                context.builder.emit(ClrOpcode::Brfalse, Some(ClrInstructionOperand::Branch(0)));
            },
            CoreInstruction::Label(label) => {
                context.builder.emit(ClrOpcode::Nop, None);
            },
            CoreInstruction::Throw => {
                context.builder.emit(ClrOpcode::Throw, None);
            },
            _ => {}
        },
        GaiaInstruction::Managed(managed) => match managed {
            crate::instruction::ManagedInstruction::CallMethod { target, method, signature, is_virtual, .. } => {
                let ret_type = gaia_type_to_clr_type(&signature.return_type);
                let param_types: Vec<ClrTypeReference> = signature.params.iter().map(gaia_type_to_clr_type).collect();
                context.builder.emit(
                    if *is_virtual {
                        ClrOpcode::Callvirt
                    } else {
                        ClrOpcode::Call
                    },
                    Some(ClrInstructionOperand::Method(
                        method.clone(),
                        Some(target.clone()),
                        param_types,
                        Box::new(ret_type),
                    )),
                );
            },
            crate::instruction::ManagedInstruction::CallStatic { target, method, signature, .. } => {
                let ret_type = gaia_type_to_clr_type(&signature.return_type);
                let param_types: Vec<ClrTypeReference> = signature.params.iter().map(gaia_type_to_clr_type).collect();
                context.builder.emit(
                    ClrOpcode::Call,
                    Some(ClrInstructionOperand::Method(
                        method.clone(),
                        Some(target.clone()),
                        param_types,
                        Box::new(ret_type),
                    )),
                );
            },
            _ => {}
        },
        _ => {}
    }
    Ok(())
}

#[cfg(feature = "clr-assembler")]
fn gaia_type_to_clr_type(t: &GaiaType) -> ClrTypeReference {
    match t {
        GaiaType::I8 => ClrTypeReference::Primitive("int8".to_string()),
        GaiaType::I16 => ClrTypeReference::Primitive("int16".to_string()),
        GaiaType::I32 => ClrTypeReference::Primitive("int32".to_string()),
        GaiaType::I64 => ClrTypeReference::Primitive("int64".to_string()),
        GaiaType::U8 => ClrTypeReference::Primitive("uint8".to_string()),
        GaiaType::U16 => ClrTypeReference::Primitive("uint16".to_string()),
        GaiaType::U32 => ClrTypeReference::Primitive("uint32".to_string()),
        GaiaType::U64 => ClrTypeReference::Primitive("uint64".to_string()),
        GaiaType::F32 => ClrTypeReference::Primitive("float32".to_string()),
        GaiaType::F64 => ClrTypeReference::Primitive("float64".to_string()),
        GaiaType::Bool => ClrTypeReference::Primitive("bool".to_string()),
        GaiaType::String => ClrTypeReference::Primitive("string".to_string()),
        GaiaType::Void => ClrTypeReference::Primitive("void".to_string()),
        GaiaType::Struct(name) => ClrTypeReference::Type(name.clone(), None),
        GaiaType::Class(name) => ClrTypeReference::Type(name.clone(), None),
        GaiaType::Array(inner, _) => ClrTypeReference::Array(Box::new(gaia_type_to_clr_type(inner))),
        _ => ClrTypeReference::Primitive("object".to_string()),
    }
}