1#![deny(clippy::all, clippy::pedantic)]
5#![allow(unused)]
6
7mod cli;
8pub use cli::main;
9
10pub use qir_backend::{
11 arrays::*, bigints::*, callables::*, exp::*, math::*, output_recording::*, range_support::*,
12 result_bool::*, strings::*, tuples::*, *,
13};
14
15use inkwell::{
16 OptimizationLevel,
17 attributes::AttributeLoc,
18 context::Context,
19 execution_engine::ExecutionEngine,
20 llvm_sys::{core::LLVMCreateMemoryBufferWithMemoryRange, ir_reader::LLVMParseIRInContext},
21 memory_buffer::MemoryBuffer,
22 module::Module,
23 passes::{PassBuilderOptions, PassManager},
24 targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple},
25 values::FunctionValue,
26};
27use std::{
28 collections::HashMap,
29 ffi::{CStr, CString, OsStr, c_char},
30 io::{Read, Write},
31 iter::once,
32 path::Path,
33 ptr::{self, NonNull, null_mut},
34};
35
36pub fn run_file(
45 path: impl AsRef<Path>,
46 entry_point: Option<&str>,
47 shots: u32,
48 rng_seed: Option<u64>,
49 output_writer: &mut impl Write,
50) -> Result<(), String> {
51 if let Some(seed) = rng_seed {
52 qir_backend::set_rng_seed(seed);
53 }
54 let context = Context::create();
55 let module = load_file(path, &context)?;
56 run_module(&module, entry_point, shots, output_writer)
57}
58
59pub fn run_input<R: Read>(
68 input: &mut R,
69 entry_point: Option<&str>,
70 shots: u32,
71 rng_seed: Option<u64>,
72 output_writer: &mut impl Write,
73) -> Result<(), String> {
74 let mut bytes = Vec::new();
75 input
76 .read_to_end(&mut bytes)
77 .map_err(|e| format!("Failed to read input: {e}"))?;
78 if bytes.is_empty() {
79 return Err("Input is empty".to_string());
80 }
81 run_bytes(&bytes, entry_point, shots, rng_seed, output_writer)
82}
83
84pub fn run_bitcode(
91 bytes: &[u8],
92 entry_point: Option<&str>,
93 shots: u32,
94 output_writer: &mut impl Write,
95) -> Result<(), String> {
96 run_bytes(bytes, entry_point, shots, None, output_writer)
97}
98
99pub fn run_bytes(
106 bytes: &[u8],
107 entry_point: Option<&str>,
108 shots: u32,
109 rng_seed: Option<u64>,
110 output_writer: &mut impl Write,
111) -> Result<(), String> {
112 if let Some(seed) = rng_seed {
113 qir_backend::set_rng_seed(seed);
114 }
115
116 let context = Context::create();
117
118 let bytes_len = bytes.len();
122 if bytes_len < 4 {
123 return Err("byte array is too short".to_string());
124 }
125 let is_bitcode =
126 bytes[0..4] == [0xDE, 0xC0, 0x17, 0x0B] || bytes[0..4] == [0x42, 0x43, 0xC0, 0xDE];
127 let bytes = if is_bitcode {
128 bytes.to_vec()
129 } else {
130 bytes.iter().copied().chain(once(0_u8)).collect()
133 };
134
135 let buffer = MemoryBuffer::create_from_memory_range(&bytes[0..bytes_len], Default::default());
136 context
137 .create_module_from_ir(buffer)
138 .map_err(|e| format!("Failed to parse module from IR: {}", e.to_string()))
139 .and_then(|module| run_module(&module, entry_point, shots, output_writer))
140}
141
142fn run_module(
143 module: &Module,
144 entry_point: Option<&str>,
145 shots: u32,
146 output_writer: &mut impl Write,
147) -> Result<(), String> {
148 module
149 .verify()
150 .map_err(|e| format!("Failed to verify module: {}", e.to_string()))?;
151
152 Target::initialize_native(&InitializationConfig::default())?;
153 let default_triple = TargetMachine::get_default_triple();
154 let target = Target::from_triple(&default_triple).map_err(|e| e.to_string())?;
155 if !target.has_asm_backend() {
156 return Err("Target doesn't have an ASM backend.".to_owned());
157 }
158 if !target.has_target_machine() {
159 return Err("Target doesn't have a target machine.".to_owned());
160 }
161
162 run_basic_passes_on(module, &default_triple, &target)?;
163
164 inkwell::support::load_library_permanently(Path::new(""));
165
166 let execution_engine = module
167 .create_jit_execution_engine(OptimizationLevel::None)
168 .map_err(|e| e.to_string())?;
169
170 bind_functions(module, &execution_engine)?;
171
172 let entry_point = choose_entry_point(module_functions(module), entry_point)?;
173 let attrs: Vec<(String, String)> = entry_point
175 .attributes(AttributeLoc::Function)
176 .iter()
177 .map(|attr| {
178 (
179 attr.get_string_kind_id()
180 .to_str()
181 .expect("Invalid UTF8 data")
182 .to_string(),
183 attr.get_string_value()
184 .to_str()
185 .expect("Invalid UTF8 data")
186 .to_string(),
187 )
188 })
189 .collect();
190
191 for _ in 1..=shots {
192 output_writer
193 .write_all("START\n".as_bytes())
194 .expect("Failed to write output");
195 for attr in &attrs {
196 output_writer
197 .write_all(format!("METADATA\t{}", attr.0).as_bytes())
198 .expect("Failed to write output");
199 if !attr.1.is_empty() {
200 output_writer
201 .write_all(format!("\t{}", attr.1).as_bytes())
202 .expect("Failed to write output");
203 }
204 output_writer
205 .write_all(qir_stdlib::output_recording::LINE_ENDING)
206 .expect("Failed to write output");
207 }
208
209 __quantum__rt__initialize(null_mut());
210 unsafe { run_entry_point(&execution_engine, entry_point)? }
211
212 OUTPUT.with(|output| {
214 let mut output = output.borrow_mut();
215 output_writer
216 .write_all(output.drain().as_slice())
217 .expect("Failed to write output");
218 });
219
220 output_writer
222 .write_all("END\t0".as_bytes())
223 .expect("Failed to write output");
224 output_writer
225 .write_all(qir_stdlib::output_recording::LINE_ENDING)
226 .expect("Failed to write output");
227 }
228 Ok(())
229}
230
231fn load_file(path: impl AsRef<Path>, context: &Context) -> Result<Module<'_>, String> {
232 let path = path.as_ref();
233 let extension = path.extension().and_then(OsStr::to_str);
234
235 match extension {
236 Some("ll") => MemoryBuffer::create_from_file(path)
237 .and_then(|buffer| context.create_module_from_ir(buffer))
238 .map_err(|e| e.to_string()),
239 Some("bc") => Module::parse_bitcode_from_path(path, context).map_err(|e| e.to_string()),
240 _ => Err(format!("Unsupported file extension '{extension:?}'.")),
241 }
242}
243
244unsafe fn run_entry_point(
245 execution_engine: &ExecutionEngine,
246 entry_point: FunctionValue,
247) -> Result<(), String> {
248 unsafe {
249 if entry_point.count_params() == 0 {
250 execution_engine.run_function(entry_point, &[]);
251 Ok(())
252 } else {
253 Err("Entry point has parameters or a non-void return type.".to_owned())
254 }
255 }
256}
257
258fn choose_entry_point<'ctx>(
259 functions: impl Iterator<Item = FunctionValue<'ctx>>,
260 name: Option<&str>,
261) -> Result<FunctionValue<'ctx>, String> {
262 let mut entry_points = functions
263 .filter(|f| is_entry_point(*f) && name.iter().all(|n| f.get_name().to_str() == Ok(n)));
264
265 let entry_point = entry_points
266 .next()
267 .ok_or_else(|| "No matching entry point found.".to_owned())?;
268
269 if entry_points.next().is_some() {
270 Err("Multiple matching entry points found.".to_owned())
271 } else {
272 Ok(entry_point)
273 }
274}
275
276fn module_functions<'ctx>(module: &Module<'ctx>) -> impl Iterator<Item = FunctionValue<'ctx>> {
277 struct FunctionValueIter<'ctx>(Option<FunctionValue<'ctx>>);
278
279 impl<'ctx> Iterator for FunctionValueIter<'ctx> {
280 type Item = FunctionValue<'ctx>;
281
282 fn next(&mut self) -> Option<Self::Item> {
283 let function = self.0;
284 self.0 = function.and_then(inkwell::values::FunctionValue::get_next_function);
285 function
286 }
287 }
288
289 FunctionValueIter(module.get_first_function())
290}
291
292fn is_entry_point(function: FunctionValue) -> bool {
293 function
294 .get_string_attribute(AttributeLoc::Function, "entry_point")
295 .is_some()
296 || function
297 .get_string_attribute(AttributeLoc::Function, "EntryPoint")
298 .is_some()
299}
300
301fn run_basic_passes_on(
302 module: &Module,
303 target_triple: &TargetTriple,
304 target: &Target,
305) -> Result<(), String> {
306 const BASIC_PASS_PIPELINE: &str = "globaldce,strip-dead-prototypes";
309
310 let target_machine = target
313 .create_target_machine(
314 target_triple,
315 "generic",
316 "",
317 OptimizationLevel::None,
318 RelocMode::Default,
319 CodeModel::Default,
320 )
321 .ok_or("Unable to create TargetMachine from Target")?;
322 module
323 .run_passes(
324 BASIC_PASS_PIPELINE,
325 &target_machine,
326 PassBuilderOptions::create(),
327 )
328 .map_err(|e| e.to_string())
329}
330
331#[allow(clippy::too_many_lines)]
332fn bind_functions(module: &Module, execution_engine: &ExecutionEngine) -> Result<(), String> {
333 let mut uses_legacy = vec![];
334 let mut declarations: HashMap<String, FunctionValue> = HashMap::default();
335 for func in module_functions(module).filter(|f| {
336 f.count_basic_blocks() == 0
337 && !f
338 .get_name()
339 .to_str()
340 .expect("Unable to coerce function name into str.")
341 .starts_with("llvm.")
342 }) {
343 declarations.insert(
344 func.get_name()
345 .to_str()
346 .expect("Unable to coerce function name into str.")
347 .to_owned(),
348 func,
349 );
350 }
351
352 macro_rules! bind {
353 ($func:ident, $param_count:expr) => {
354 if let Some(func) = declarations.get(stringify!($func)) {
355 if func.get_params().len() != $param_count {
356 return Err(format!(
357 "Function '{}' has mismatched parameters: expected {}, found {}",
358 stringify!($func),
359 $param_count,
360 func.get_params().len()
361 ));
362 }
363 execution_engine.add_global_mapping(func, $func as *const () as usize);
364 declarations.remove(stringify!($func));
365 }
366 };
367 }
368
369 macro_rules! bind_controlled {
370 ($legacy:ident, $flat:ident, $flat_count:expr) => {
371 if let Some(func) = declarations.get(stringify!($legacy)) {
372 let count = func.get_params().len();
373 let implementation = if count == 2 {
374 $legacy as *const () as usize
375 } else if count == $flat_count {
376 $flat as *const () as usize
377 } else {
378 return Err(format!(
379 "Function '{}' has mismatched parameters: expected 2 or {}, found {}",
380 stringify!($legacy),
381 $flat_count,
382 count
383 ));
384 };
385 execution_engine.add_global_mapping(func, implementation);
386 declarations.remove(stringify!($legacy));
387 }
388 };
389 }
390
391 macro_rules! legacy_output {
392 ($func:ident) => {
393 if let Some(func) = declarations.get(stringify!($func)) {
394 execution_engine.add_global_mapping(
395 func,
396 qir_backend::output_recording::legacy::$func as *const () as usize,
397 );
398 declarations.remove(stringify!($func));
399 Some(true)
400 } else {
401 None
402 }
403 };
404 }
405
406 macro_rules! bind_output_record {
407 ($func:ident) => {
408 if let Some(func) = declarations.get(stringify!($func)) {
409 if func.get_params().len() == 1 {
410 execution_engine.add_global_mapping(
411 func,
412 qir_backend::output_recording::legacy::$func as *const () as usize,
413 );
414 declarations.remove(stringify!($func));
415 Some(true)
416 } else {
417 execution_engine.add_global_mapping(func, $func as *const () as usize);
418 declarations.remove(stringify!($func));
419 Some(false)
420 }
421 } else {
422 None
423 }
424 };
425 }
426
427 uses_legacy.push(legacy_output!(__quantum__rt__array_end_record_output));
429 uses_legacy.push(legacy_output!(__quantum__rt__array_start_record_output));
430 uses_legacy.push(legacy_output!(__quantum__rt__tuple_end_record_output));
431 uses_legacy.push(legacy_output!(__quantum__rt__tuple_start_record_output));
432
433 bind!(__quantum__rt__initialize, 1);
434 bind!(__quantum__qis__arccos__body, 1);
435 bind!(__quantum__qis__arcsin__body, 1);
436 bind!(__quantum__qis__arctan__body, 1);
437 bind!(__quantum__qis__arctan2__body, 2);
438 bind!(__quantum__qis__assertmeasurementprobability__body, 6);
439 bind!(__quantum__qis__assertmeasurementprobability__ctl, 6);
440 bind!(__quantum__qis__barrier__body, 0);
441 bind!(__quantum__qis__ccx__body, 3);
442 bind!(__quantum__qis__cnot__body, 2);
443 bind!(__quantum__qis__cos__body, 1);
444 bind!(__quantum__qis__cosh__body, 1);
445 bind!(__quantum__qis__cx__body, 2);
446 bind!(__quantum__qis__cy__body, 2);
447 bind!(__quantum__qis__cz__body, 2);
448 bind!(__quantum__qis__drawrandomdouble__body, 2);
449 bind!(__quantum__qis__drawrandomint__body, 2);
450 bind!(__quantum__qis__dumpmachine__body, 1);
451 bind!(__quantum__qis__exp__body, 3);
452 bind!(__quantum__qis__exp__adj, 3);
453 bind_controlled!(__quantum__qis__exp__ctl, __quantum__qis__exp__ctl_flat, 4);
454 bind_controlled!(
455 __quantum__qis__exp__ctladj,
456 __quantum__qis__exp__ctladj_flat,
457 4
458 );
459 bind!(__quantum__qis__h__body, 1);
460 bind!(__quantum__qis__h__ctl, 2);
461 bind!(__quantum__qis__ieeeremainder__body, 2);
462 bind!(__quantum__qis__infinity__body, 0);
463 bind!(__quantum__qis__isinf__body, 1);
464 bind!(__quantum__qis__isnan__body, 1);
465 bind!(__quantum__qis__isnegativeinfinity__body, 1);
466 bind!(__quantum__qis__log__body, 1);
467 bind!(__quantum__qis__measure__body, 2);
468 bind!(__quantum__qis__mresetz__body, 2);
469 bind!(__quantum__qis__mz__body, 2);
470 bind!(__quantum__qis__nan__body, 0);
471 bind!(__quantum__qis__r__adj, 3);
472 bind!(__quantum__qis__r__body, 3);
473 bind_controlled!(__quantum__qis__r__ctl, __quantum__qis__r__ctl_flat, 4);
474 bind_controlled!(__quantum__qis__r__ctladj, __quantum__qis__r__ctladj_flat, 4);
475 bind!(__quantum__qis__read_result__body, 1);
476 bind!(__quantum__qis__reset__body, 1);
477 bind!(__quantum__qis__rx__body, 2);
478 bind_controlled!(__quantum__qis__rx__ctl, __quantum__qis__rx__ctl_flat, 3);
479 bind!(__quantum__qis__rxx__body, 3);
480 bind!(__quantum__qis__ry__body, 2);
481 bind_controlled!(__quantum__qis__ry__ctl, __quantum__qis__ry__ctl_flat, 3);
482 bind!(__quantum__qis__ryy__body, 3);
483 bind!(__quantum__qis__rz__body, 2);
484 bind_controlled!(__quantum__qis__rz__ctl, __quantum__qis__rz__ctl_flat, 3);
485 bind!(__quantum__qis__rzz__body, 3);
486 bind!(__quantum__qis__s__adj, 1);
487 bind!(__quantum__qis__s__body, 1);
488 bind!(__quantum__qis__s__ctl, 2);
489 bind!(__quantum__qis__s__ctladj, 2);
490 bind!(__quantum__qis__sx__body, 1);
491 bind!(__quantum__qis__sin__body, 1);
492 bind!(__quantum__qis__sinh__body, 1);
493 bind!(__quantum__qis__sqrt__body, 1);
494 bind!(__quantum__qis__swap__body, 2);
495 bind!(__quantum__qis__t__adj, 1);
496 bind!(__quantum__qis__t__body, 1);
497 bind!(__quantum__qis__t__ctl, 2);
498 bind!(__quantum__qis__t__ctladj, 2);
499 bind!(__quantum__qis__tan__body, 1);
500 bind!(__quantum__qis__tanh__body, 1);
501 bind!(__quantum__qis__x__body, 1);
502 bind!(__quantum__qis__x__ctl, 2);
503 bind!(__quantum__qis__y__body, 1);
504 bind!(__quantum__qis__y__ctl, 2);
505 bind!(__quantum__qis__z__body, 1);
506 bind!(__quantum__qis__z__ctl, 2);
507 bind!(__quantum__rt__array_concatenate, 2);
508 bind!(__quantum__rt__array_copy, 2);
509 bind!(__quantum__rt__array_create_1d, 2);
510
511 bind!(__quantum__rt__array_record_output, 2);
513 bind!(__quantum__rt__tuple_record_output, 2);
514
515 uses_legacy.push(bind_output_record!(__quantum__rt__bool_record_output));
517 uses_legacy.push(bind_output_record!(__quantum__rt__double_record_output));
518 uses_legacy.push(bind_output_record!(__quantum__rt__int_record_output));
519
520 uses_legacy.push(
522 if let Some(func) = declarations.get("__quantum__rt__result_record_output") {
523 if func.get_params().len() == 1 {
524 execution_engine.add_global_mapping(
525 func,
526 qir_backend::legacy_output::__quantum__rt__result_record_output as *const ()
527 as usize,
528 );
529 declarations.remove("__quantum__rt__result_record_output");
530 Some(true)
531 } else {
532 execution_engine.add_global_mapping(
533 func,
534 __quantum__rt__result_record_output as *const () as usize,
535 );
536 declarations.remove("__quantum__rt__result_record_output");
537 Some(false)
538 }
539 } else {
540 None
541 },
542 );
543
544 if let Some(func) = declarations.get("__quantum__qis__m__body") {
547 if func.get_params().len() == 2 {
548 execution_engine.add_global_mapping(
549 func,
550 qir_backend::__quantum__qis__mz__body as *const () as usize,
551 );
552 } else if func.get_params().len() == 1 {
553 execution_engine.add_global_mapping(
554 func,
555 qir_backend::__quantum__qis__m__body as *const () as usize,
556 );
557 } else {
558 return Err(format!(
559 "Function '__quantum__qis__m__body' has mismatched parameters: expected 1 or 2, found {}",
560 func.get_params().len()
561 ));
562 }
563 declarations.remove("__quantum__qis__m__body");
564 }
565
566 bind!(__quantum__rt__array_get_element_ptr_1d, 2);
567 bind!(__quantum__rt__array_get_size_1d, 1);
568 bind!(quantum__rt__array_slice_1d, 3);
569 bind!(__quantum__rt__array_update_alias_count, 2);
570 bind!(__quantum__rt__array_update_reference_count, 2);
571 bind!(__quantum__rt__bigint_add, 2);
572 bind!(__quantum__rt__bigint_bitand, 2);
573 bind!(__quantum__rt__bigint_bitnot, 1);
574 bind!(__quantum__rt__bigint_bitor, 2);
575 bind!(__quantum__rt__bigint_bitxor, 2);
576 bind!(__quantum__rt__bigint_create_array, 2);
577 bind!(__quantum__rt__bigint_create_i64, 1);
578 bind!(__quantum__rt__bigint_divide, 2);
579 bind!(__quantum__rt__bigint_equal, 2);
580 bind!(__quantum__rt__bigint_get_data, 1);
581 bind!(__quantum__rt__bigint_get_length, 1);
582 bind!(__quantum__rt__bigint_greater, 2);
583 bind!(__quantum__rt__bigint_greater_eq, 2);
584 bind!(__quantum__rt__bigint_modulus, 2);
585 bind!(__quantum__rt__bigint_multiply, 2);
586 bind!(__quantum__rt__bigint_negate, 1);
587 bind!(__quantum__rt__bigint_power, 2);
588 bind!(__quantum__rt__bigint_shiftleft, 2);
589 bind!(__quantum__rt__bigint_shiftright, 2);
590 bind!(__quantum__rt__bigint_subtract, 2);
591 bind!(__quantum__rt__bigint_to_string, 1);
592 bind!(__quantum__rt__bigint_update_reference_count, 2);
593 bind!(__quantum__rt__bool_to_string, 1);
594 bind!(__quantum__rt__callable_copy, 2);
595 bind!(__quantum__rt__callable_create, 3);
596 bind!(__quantum__rt__callable_invoke, 3);
597 bind!(__quantum__rt__callable_make_adjoint, 1);
598 bind!(__quantum__rt__callable_make_controlled, 1);
599 bind!(__quantum__rt__callable_update_alias_count, 2);
600 bind!(__quantum__rt__callable_update_reference_count, 2);
601 bind!(__quantum__rt__capture_update_alias_count, 2);
602 bind!(__quantum__rt__capture_update_reference_count, 2);
603 bind!(__quantum__rt__double_to_string, 1);
604 bind!(__quantum__rt__fail, 1);
605 bind!(__quantum__rt__int_to_string, 1);
606 bind!(__quantum__rt__memory_allocate, 1);
607 bind!(__quantum__rt__message, 1);
608 bind!(__quantum__rt__pauli_to_string, 1);
609 bind!(__quantum__rt__qubit_allocate, 0);
610 bind!(__quantum__rt__qubit_allocate_array, 1);
611 bind!(__quantum__rt__qubit_release, 1);
612 bind!(__quantum__rt__qubit_release_array, 1);
613 bind!(__quantum__rt__qubit_to_string, 1);
614 bind!(__quantum__rt__read_result, 1);
615 bind!(__quantum__rt__result_equal, 2);
616 bind!(quantum__rt__range_to_string, 1);
617 bind!(__quantum__rt__result_get_one, 0);
618 bind!(__quantum__rt__result_get_zero, 0);
619 bind!(__quantum__rt__result_to_string, 1);
620 bind!(__quantum__rt__result_update_reference_count, 2);
621 bind!(__quantum__rt__string_concatenate, 2);
622 bind!(__quantum__rt__string_create, 1);
623 bind!(__quantum__rt__string_equal, 2);
624 bind!(__quantum__rt__string_get_data, 1);
625 bind!(__quantum__rt__string_get_length, 1);
626 bind!(__quantum__rt__string_update_reference_count, 2);
627 bind!(__quantum__rt__tuple_copy, 2);
628 bind!(__quantum__rt__tuple_create, 1);
629 bind!(__quantum__rt__tuple_update_alias_count, 2);
630 bind!(__quantum__rt__tuple_update_reference_count, 2);
631 bind!(__quantum__rt__write_result, 2);
632
633 if !(uses_legacy.iter().filter_map(|&b| b).all(|b| b)
634 || uses_legacy.iter().filter_map(|&b| b).all(|b| !b))
635 {
636 Err("Use of legacy and current output recording functions in the same program is not supported".to_string())
637 } else if declarations.is_empty() {
638 Ok(())
639 } else {
640 let keys = declarations.keys().collect::<Vec<_>>();
641 let (first, rest) = keys
642 .split_first()
643 .expect("Declarations list should be non-empty.");
644 Err(format!(
645 "Failed to link some declared functions: {}",
646 rest.iter().fold((*first).clone(), |mut accum, f| {
647 accum.push_str(", ");
648 accum.push_str(f);
649 accum
650 })
651 ))
652 }
653}