runner/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![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
36/// # Errors
37///
38/// Will return `Err` if
39/// - `filename` does not exist or the user does not have permission to read it.
40/// - `filename` does not contain a valid bitcode module
41/// - `filename` does not have either a .ll or .bc as an extension
42/// - `entry_point` is not found in the QIR
43/// - Entry point has parameters or a non-void return type.
44pub 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
59/// # Errors
60///
61/// Will return `Err` if
62/// - The input cannot be read from `input`.
63/// - The input is empty.
64/// - The input does not contain a valid bitcode module or LLVM IR string.
65/// - `entry_point` is not found in the QIR.
66/// - Entry point has parameters or a non-void return type.
67pub 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
84/// # Errors
85///
86/// Will return `Err` if
87/// - `bytes` does not contain a valid bitcode module
88/// - `entry_point` is not found in the QIR
89/// - Entry point has parameters or a non-void return type.
90pub 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
99/// # Errors
100///
101/// Will return `Err` if
102/// - `bytes` does not contain a valid bitcode module or LLVM IR string
103/// - `entry_point` is not found in the QIR
104/// - Entry point has parameters or a non-void return type.
105pub 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    // To know if the bytes are bitcode, check for both the wrapped and non-wrapped magic bytes.
119    // See the definition for llvm::isBitCode at https://llvm.org/doxygen/namespacellvm.html#ae0ccf1c0633b02c90c21118d0c1c7ec4
120    // for reference.
121    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        // The bytes represent LLVM IR string, so we must ensure it is null-terminated.
131        // Note that we use the original bytes length to avoid including the null terminator in the IR parsing, which would cause it to fail.
132        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    // TODO: need a cleaner way to get the attr strings for metadata
174    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        // Write the saved output records to the output_writer
213        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        // Write the end of the shot
221        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    // Description of this syntax:
307    // https://github.com/llvm/llvm-project/blob/2ba08386156ef25913b1bee170d8fe95aaceb234/llvm/include/llvm/Passes/PassBuilder.h#L308-L347
308    const BASIC_PASS_PIPELINE: &str = "globaldce,strip-dead-prototypes";
309
310    // Boilerplate taken from here:
311    // https://github.com/TheDan64/inkwell/blob/5c9f7fcbb0a667f7391b94beb65f1a670ad13221/examples/kaleidoscope/main.rs#L86-L95
312    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    // Legacy output methods
428    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    // New calls
512    bind!(__quantum__rt__array_record_output, 2);
513    bind!(__quantum__rt__tuple_record_output, 2);
514
515    // calls with unlabeled signature variants
516    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    // results need special handling as they aren't in the std lib
521    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    // calls to __quantum__qis__m__body may use either dynamic or static results, so bind to the right
545    // implementation based on number of arguments.
546    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}