1#[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#[derive(Default)]
24#[cfg(feature = "clr-assembler")]
25pub struct ClrBackend {}
26
27#[cfg(feature = "clr-assembler")]
28impl ClrBackend {
29 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 let has_main = program.functions.iter().any(|f| f.name == "main");
36
37 let result = if has_main {
38 let writer = clr_assembler::formats::exe::writer::DotNetWriter::new(buffer);
40 writer.write(&clr_program)
41 } else {
42 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 for import in &program.imports {
142 context.builder.add_external_assembly(import.library.clone());
143 }
144
145 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 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 _ => {} }
168 }
169 }
170 }
171
172 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 _ => {} }
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}