1use inkwell::AddressSpace;
2use inkwell::builder::Builder;
3use inkwell::values::FloatValue;
4use inkwell::values::FunctionValue;
5
6use coe::is_same;
7use qudit_core::RealScalar;
8use std::collections::HashMap;
9
10use crate::ComplexExpression;
11use crate::Expression;
12
13use super::builtins::Builtins;
14use super::module::Module;
15
16#[derive(Debug)]
17pub struct CodeGenError {
18 pub message: String,
19}
20
21impl CodeGenError {
22 pub fn new(message: &str) -> Self {
23 CodeGenError {
24 message: message.to_string(),
25 }
26 }
27}
28
29type CodeGenResult<T> = Result<T, CodeGenError>;
30
31pub struct CodeGenerator<'ctx, R: RealScalar> {
32 pub context: &'ctx Module<R>,
33 pub builder: Builder<'ctx>,
34
35 variables: HashMap<String, FloatValue<'ctx>>,
36 expressions: HashMap<String, FloatValue<'ctx>>,
37 functions: HashMap<String, FunctionValue<'ctx>>,
38 fn_value_opt: Option<FunctionValue<'ctx>>,
39 output_ptr_idx: Option<u32>,
40}
41
42impl<'ctx, R: RealScalar> CodeGenerator<'ctx, R> {
43 pub fn new(context: &'ctx Module<R>) -> Self {
44 let builder = context.context().create_builder();
45 CodeGenerator {
46 context,
47 builder,
48 variables: HashMap::new(),
49 functions: HashMap::new(),
50 expressions: HashMap::new(),
51 fn_value_opt: None,
52 output_ptr_idx: None,
53 }
54 }
55
56 fn int_type(&self) -> inkwell::types::IntType<'ctx> {
57 if is_same::<R, f32>() {
58 self.context.context().i32_type()
59 } else if is_same::<R, f64>() {
60 self.context.context().i64_type()
61 } else {
62 panic!("Unknown bit width");
63 }
64 }
65
66 fn float_type(&self) -> inkwell::types::FloatType<'ctx> {
67 if is_same::<R, f32>() {
68 self.context.context().f32_type()
69 } else if is_same::<R, f64>() {
70 self.context.context().f64_type()
71 } else {
72 panic!("Unknown bit width");
73 }
74 }
75
76 fn gen_write_func_proto(&self, name: &str) -> CodeGenResult<FunctionValue<'ctx>> {
77 let ret_type = self.context.context().void_type();
78 let ptr_type = self.context.context().ptr_type(AddressSpace::default());
79 let param_types = vec![
81 ptr_type.into(), ptr_type.into(), ptr_type.into(), ptr_type.into(), self.context.context().i64_type().into(), ptr_type.into(), ];
88 let func_type = ret_type.fn_type(¶m_types, false);
89 let func = self
90 .context
91 .with_module(|module| module.add_function(name, func_type, None));
92 Ok(func)
93 }
94
95 fn build_expression(&mut self, expr: &Expression) -> CodeGenResult<FloatValue<'ctx>> {
96 let expr_str = expr.to_string();
97 let cached = self.expressions.get(&expr_str);
98 if let Some(c) = cached {
99 return Ok(*c);
100 }
101
102 let val = match expr {
103 Expression::Pi => Ok(self.float_type().const_float(std::f64::consts::PI)),
104 Expression::Constant(_) => Ok(self.float_type().const_float(expr.to_float())),
105 Expression::Variable(name) => self
106 .variables
107 .get(name)
108 .ok_or(CodeGenError::new(&format!("Variable {} not found", name)))
109 .copied(),
110 Expression::Neg(expr) => {
111 let val = self.build_expression(expr)?;
112 Ok(self.builder.build_float_neg(val, "tmp").unwrap())
113 }
114 Expression::Add(lhs, rhs) => {
115 let lhs_val = self.build_expression(lhs)?;
116 let rhs_val = self.build_expression(rhs)?;
117 Ok(self
118 .builder
119 .build_float_add(lhs_val, rhs_val, "tmp")
120 .unwrap())
121 }
122 Expression::Sub(lhs, rhs) => {
123 let lhs_val = self.build_expression(lhs)?;
124 let rhs_val = self.build_expression(rhs)?;
125 Ok(self
126 .builder
127 .build_float_sub(lhs_val, rhs_val, "tmp")
128 .unwrap())
129 }
130 Expression::Mul(lhs, rhs) => {
131 let lhs_val = self.build_expression(lhs)?;
132 let rhs_val = self.build_expression(rhs)?;
133 Ok(self
134 .builder
135 .build_float_mul(lhs_val, rhs_val, "tmp")
136 .unwrap())
137 }
138 Expression::Div(lhs, rhs) => {
139 let lhs_val = self.build_expression(lhs)?;
140 let rhs_val = self.build_expression(rhs)?;
141 Ok(self
142 .builder
143 .build_float_div(lhs_val, rhs_val, "tmp")
144 .unwrap())
145 }
146 Expression::Pow(base, exponent) => {
147 let base_val = self.build_expression(base)?;
148 let exponent_val = self.build_expression(exponent)?;
149 let pow = self.get_builtin("pow");
150 let args = [base_val.into(), exponent_val.into()];
151 let val = self
152 .builder
153 .build_call(pow, &args, "tmp")
154 .unwrap()
155 .try_as_basic_value()
156 .basic()
157 .unwrap()
158 .into_float_value();
159 Ok(val)
160 }
161 Expression::Sqrt(expr) => {
162 let arg = self.build_expression(expr)?;
163 let sqrt = self.get_builtin("sqrt");
164 let val = self
165 .builder
166 .build_call(sqrt, &[arg.into()], "tmp")
167 .unwrap()
168 .try_as_basic_value()
169 .basic()
170 .unwrap()
171 .into_float_value();
172 Ok(val)
173 }
174 Expression::Sin(expr) => {
175 let arg = self.build_expression(expr)?;
176 let sin = self.get_builtin("sin");
177 let val = self
178 .builder
179 .build_call(sin, &[arg.into()], "tmp")
180 .unwrap()
181 .try_as_basic_value()
182 .basic()
183 .unwrap()
184 .into_float_value();
185 Ok(val)
186 }
187 Expression::Cos(expr) => {
188 let arg = self.build_expression(expr)?;
189 let cos = self.get_builtin("cos");
190 let val = self
191 .builder
192 .build_call(cos, &[arg.into()], "tmp")
193 .unwrap()
194 .try_as_basic_value()
195 .basic()
196 .unwrap()
197 .into_float_value();
198 Ok(val)
199 }
200 };
201
202 if let Ok(val) = val {
203 self.expressions.insert(expr_str, val);
204 }
205 val
206 }
207
208 pub fn compile_expr(
209 &mut self,
210 expr: &ComplexExpression,
211 re_offset: usize,
212 need_to_write_real_zero: bool,
213 ) -> CodeGenResult<()> {
214 let re_offset: u64 = re_offset as u64;
215 let ptr_idx = self
216 .output_ptr_idx
217 .to_owned()
218 .expect("Output pointer index not set");
219 let ptr = self
220 .fn_value_opt
221 .unwrap()
222 .get_nth_param(ptr_idx)
223 .unwrap()
224 .into_pointer_value();
225
226 if need_to_write_real_zero || !expr.real.is_zero_fast() {
227 let val = self.build_expression(&expr.real)?;
228 let offset = self.int_type().const_int(re_offset, false);
229 let offset_ptr = unsafe {
230 self.builder
231 .build_gep(self.float_type(), ptr, &[offset], "offset_ptr")
232 .unwrap()
233 };
234
235 match self.builder.build_store(offset_ptr, val) {
236 Ok(_) => {}
237 Err(e) => {
238 return Err(CodeGenError::new(&format!("Error storing value: {}", e)));
239 }
240 };
241 }
242
243 if !expr.imag.is_zero_fast() {
244 let val = self.build_expression(&expr.imag)?;
245 let offset = self.int_type().const_int(re_offset + 1, false);
246 let offset_ptr = unsafe {
247 self.builder
248 .build_gep(self.float_type(), ptr, &[offset], "offset_ptr")
249 .unwrap()
250 };
251
252 match self.builder.build_store(offset_ptr, val) {
253 Ok(_) => {}
254 Err(e) => {
255 return Err(CodeGenError::new(&format!("Error storing value: {}", e)));
256 }
257 };
258 }
259
260 Ok(())
261 }
262
263 fn build_var_table(&mut self, variables: &[String]) {
264 self.variables.clear();
265 let params_ptr = self
266 .fn_value_opt
267 .unwrap()
268 .get_nth_param(0)
269 .unwrap()
270 .into_pointer_value();
271 let param_offset_ptr = self
272 .fn_value_opt
273 .unwrap()
274 .get_nth_param(2)
275 .unwrap()
276 .into_pointer_value();
277
278 for (map_idx, var_name) in variables.iter().enumerate() {
279 let map_idx_val = self
282 .context
283 .context()
284 .i64_type()
285 .const_int(map_idx as u64, false);
286 let actual_offset_ptr = unsafe {
287 self.builder
288 .build_gep(
289 self.context.context().i64_type(),
290 param_offset_ptr,
291 &[map_idx_val],
292 "actual_offset_ptr_gep",
293 )
294 .unwrap()
295 };
296 let actual_offset_val = self
297 .builder
298 .build_load(
299 self.context.context().i64_type(),
300 actual_offset_ptr,
301 "actual_offset_val",
302 )
303 .unwrap()
304 .into_int_value();
305
306 let var_ptr = unsafe {
308 self.builder
309 .build_gep(
310 self.float_type(),
311 params_ptr,
312 &[actual_offset_val],
313 "var_ptr_gep",
314 )
315 .unwrap()
316 };
317
318 let val = self
319 .builder
320 .build_load(self.float_type(), var_ptr, var_name)
321 .unwrap()
322 .into_float_value();
323 self.variables.insert(var_name.to_owned(), val);
324 }
325 }
326
327 fn get_builtin(&mut self, name: &str) -> FunctionValue<'ctx> {
328 if let Some(f) = self.functions.get(name) {
329 return *f;
330 }
331
332 let b = match Builtins::from_str(name) {
333 Some(b) => b,
334 None => {
335 panic!("Unsupported builtin function: {}", name);
336 }
337 };
338
339 let intr = match b.intrinsic() {
340 Some(i) => i,
341 None => {
342 panic!("Unsupported builtin function: {}", name);
343 }
344 };
345
346 let decl = self
347 .context
348 .with_module(|module| intr.get_declaration(&module, &[self.float_type().into()]));
349
350 let fn_value = match decl {
351 Some(f) => f,
352 None => {
353 panic!("Unsupported builtin function: {}", name);
354 }
355 };
356
357 self.functions.insert(name.to_string(), fn_value);
358 fn_value
359 }
360
361 pub fn gen_func(
382 &mut self,
383 fn_name: &str,
384 fn_expr: &[Expression],
385 var_table: &[String],
386 fn_len: usize,
387 ) -> CodeGenResult<()> {
388 self.expressions.clear();
389 let func = self.gen_write_func_proto(fn_name)?;
390 let entry = self.context.context().append_basic_block(func, "entry");
391 self.builder.position_at_end(entry);
392 self.fn_value_opt = Some(func);
393 self.build_var_table(var_table);
395
396 let output_ptr = self
397 .fn_value_opt
398 .unwrap()
399 .get_nth_param(1)
400 .unwrap()
401 .into_pointer_value();
402 let output_map_ptr = self
403 .fn_value_opt
404 .unwrap()
405 .get_nth_param(3)
406 .unwrap()
407 .into_pointer_value();
408 let fn_unit_offset = self
409 .fn_value_opt
410 .unwrap()
411 .get_nth_param(4)
412 .unwrap()
413 .into_int_value();
414 let const_param_ptr = self
415 .fn_value_opt
416 .unwrap()
417 .get_nth_param(5)
418 .unwrap()
419 .into_pointer_value();
420 let int_increment = self.context.context().i64_type().const_int(1u64, false);
421 let mut dyn_fn_unit_idx = self.context.context().i64_type().const_int(0u64, false);
422
423 for (fn_unit_idx, fn_unit_exprs) in fn_expr.chunks(fn_len).enumerate() {
424 let current_fn = self.fn_value_opt.unwrap();
425 let compute_block = self
426 .context
427 .context()
428 .append_basic_block(current_fn, &format!("compute_unit_{}", fn_unit_idx));
429 let skip_block = self
430 .context
431 .context()
432 .append_basic_block(current_fn, &format!("skip_unit_{}", fn_unit_idx));
433
434 if fn_unit_idx == 0 {
435 self.builder
437 .build_unconditional_branch(compute_block)
438 .unwrap();
439 } else if fn_unit_idx <= var_table.len() {
440 let param_idx_for_const_check = self
443 .context
444 .context()
445 .i64_type()
446 .const_int((fn_unit_idx - 1) as u64, false);
447 let const_param_elem_ptr = unsafe {
448 self.builder
449 .build_gep(
450 self.context.context().i8_type(),
451 const_param_ptr,
452 &[param_idx_for_const_check],
453 "const_param_elem_ptr",
454 )
455 .unwrap()
456 };
457 let is_constant_val = self
458 .builder
459 .build_load(
460 self.context.context().i8_type(),
461 const_param_elem_ptr,
462 "is_constant",
463 )
464 .unwrap()
465 .into_int_value();
466
467 let is_constant_true = self
468 .builder
469 .build_int_compare(
470 inkwell::IntPredicate::EQ,
471 is_constant_val,
472 self.context.context().i8_type().const_int(1, false),
473 "is_constant_true",
474 )
475 .unwrap();
476
477 self.builder
479 .build_conditional_branch(is_constant_true, skip_block, compute_block)
480 .unwrap();
481 } else {
482 let index = fn_unit_idx - var_table.len() - 1;
485 let param_j = ((((8 * index + 1) as f64).sqrt().floor() as usize) - 1) / 2;
486 let param_i = index - param_j * (param_j + 1) / 2;
487
488 let param_idx_i_for_const_check = self
490 .context
491 .context()
492 .i64_type()
493 .const_int((param_i - 1) as u64, false);
494 let const_param_elem_ptr = unsafe {
495 self.builder
496 .build_gep(
497 self.context.context().i8_type(),
498 const_param_ptr,
499 &[param_idx_i_for_const_check],
500 "const_param_i_elem_ptr",
501 )
502 .unwrap()
503 };
504 let is_i_constant_val = self
505 .builder
506 .build_load(
507 self.context.context().i8_type(),
508 const_param_elem_ptr,
509 "is_i_constant",
510 )
511 .unwrap()
512 .into_int_value();
513
514 let is_i_constant_true = self
515 .builder
516 .build_int_compare(
517 inkwell::IntPredicate::EQ,
518 is_i_constant_val,
519 self.context.context().i8_type().const_int(1, false),
520 "is_i_constant_true",
521 )
522 .unwrap();
523
524 let param_idx_j_for_const_check = self
526 .context
527 .context()
528 .i64_type()
529 .const_int((param_j - 1) as u64, false);
530 let const_param_elem_ptr = unsafe {
531 self.builder
532 .build_gep(
533 self.context.context().i8_type(),
534 const_param_ptr,
535 &[param_idx_j_for_const_check],
536 "const_param_j_elem_ptr",
537 )
538 .unwrap()
539 };
540 let is_j_constant_val = self
541 .builder
542 .build_load(
543 self.context.context().i8_type(),
544 const_param_elem_ptr,
545 "is_j_constant",
546 )
547 .unwrap()
548 .into_int_value();
549
550 let is_j_constant_true = self
551 .builder
552 .build_int_compare(
553 inkwell::IntPredicate::EQ,
554 is_j_constant_val,
555 self.context.context().i8_type().const_int(1, false),
556 "is_j_constant_true",
557 )
558 .unwrap();
559
560 let is_either_constant = self
561 .builder
562 .build_or(is_i_constant_true, is_j_constant_true, "is_either_constant")
563 .unwrap();
564
565 self.builder
567 .build_conditional_branch(is_either_constant, skip_block, compute_block)
568 .unwrap();
569 }
570
571 self.builder.position_at_end(compute_block);
573
574 let this_unit_offset = self
575 .builder
576 .build_int_mul(fn_unit_offset, dyn_fn_unit_idx, "this_unit_offset")
577 .unwrap();
578 dyn_fn_unit_idx = self
579 .builder
580 .build_int_add(dyn_fn_unit_idx, int_increment, "dyn_fn_unit_idx")
581 .unwrap();
582
583 for (i, expr) in fn_unit_exprs.iter().enumerate() {
584 if expr.is_zero_fast() {
585 continue;
586 }
587
588 let val = self.build_expression(expr)?;
589 let offset_ptr = unsafe {
590 let output_idx = self.context.context().i64_type().const_int(i as u64, false);
591 let output_map_elem_ptr = self
592 .builder
593 .build_gep(
594 self.context.context().i64_type(),
595 output_map_ptr,
596 &[output_idx],
597 "output_map_elem_ptr",
598 )
599 .unwrap();
600 let output_map_offset = self
601 .builder
602 .build_load(
603 self.context.context().i64_type(),
604 output_map_elem_ptr,
605 "output_map_offset",
606 )
607 .unwrap()
608 .into_int_value();
609 let combined_offset = self
610 .builder
611 .build_int_add(output_map_offset, this_unit_offset, "combined_offset")
612 .unwrap();
613 self.builder
614 .build_gep(
615 self.float_type(),
616 output_ptr,
617 &[combined_offset],
618 "offset_ptr",
619 )
620 .unwrap()
621 };
622
623 match self.builder.build_store(offset_ptr, val) {
624 Ok(_) => {}
625 Err(e) => {
626 return Err(CodeGenError::new(&format!("Error storing value: {}", e)));
627 }
628 };
629 }
630
631 self.builder.build_unconditional_branch(skip_block).unwrap();
634
635 self.builder.position_at_end(skip_block);
637 }
638
639 match self.builder.build_return(None) {
640 Ok(_) => Ok(()),
641 Err(e) => Err(CodeGenError::new(&format!("Error building return: {}", e))),
642 }
643 }
644}