#[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;
#[derive(Default)]
#[cfg(feature = "clr-assembler")]
pub struct ClrBackend {}
#[cfg(feature = "clr-assembler")]
impl ClrBackend {
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());
let has_main = program.functions.iter().any(|f| f.name == "main");
let result = if has_main {
let writer = clr_assembler::formats::exe::writer::DotNetWriter::new(buffer);
writer.write(&clr_program)
} else {
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();
}
for import in &program.imports {
context.builder.add_external_assembly(import.library.clone());
}
let default_class_name = if program.name.is_empty() { "Program" } else { &program.name };
context.builder.begin_class(default_class_name.to_string(), None);
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);
}
_ => {} }
}
}
}
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())),
)),
);
}
_ => {} }
}
}
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()),
}
}