microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billt/revert-mimalloc

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir_base.rs

579lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use num_bigint::BigUint;
8use num_complex::Complex;
9use qsc_data_structures::index_map::IndexMap;
10use qsc_eval::{
11 backend::Backend,
12 debug::{map_hir_package_to_fir, Frame},
13 eval,
14 output::GenericReceiver,
15 val::Value,
16 Env, Error,
17};
18use qsc_fir::fir;
19use qsc_frontend::compile::PackageStore;
20use qsc_hir::hir::{self};
21use rustc_hash::FxHashSet;
22use std::fmt::{Display, Write};
23
24/// # Errors
25///
26/// This function will return an error if execution was unable to complete.
27/// # Panics
28///
29/// This function will panic if compiler state is invalid or in out-of-memory conditions.
30pub fn generate_qir(
31 store: &PackageStore,
32 package: hir::PackageId,
33) -> std::result::Result<String, (Error, Vec<Frame>)> {
34 let mut fir_lowerer = qsc_eval::lower::Lowerer::new();
35 let mut fir_store = fir::PackageStore::new();
36 for (id, unit) in store {
37 fir_store.insert(
38 map_hir_package_to_fir(id),
39 fir_lowerer.lower_package(&unit.package),
40 );
41 }
42
43 let package = map_hir_package_to_fir(package);
44 let unit = fir_store.get(package);
45
46 let mut sim = BaseProfSim::default();
47 let mut stdout = std::io::sink();
48 let mut out = GenericReceiver::new(&mut stdout);
49 let result = eval(
50 package,
51 None,
52 unit.entry_exec_graph.clone(),
53 &fir_store,
54 &mut Env::default(),
55 &mut sim,
56 &mut out,
57 );
58 match result {
59 Ok(val) => Ok(sim.finish(&val)),
60 Err((err, stack)) => Err((err, stack)),
61 }
62}
63
64#[derive(Copy, Clone, Default)]
65struct HardwareId(usize);
66
67pub struct BaseProfSim {
68 next_meas_id: usize,
69 next_qubit_id: usize,
70 next_qubit_hardware_id: HardwareId,
71 qubit_map: IndexMap<usize, HardwareId>,
72 instrs: String,
73 measurements: String,
74 decls: String,
75 decl_names: FxHashSet<String>,
76}
77
78impl Default for BaseProfSim {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl BaseProfSim {
85 #[must_use]
86 pub fn new() -> Self {
87 let mut sim = BaseProfSim {
88 next_meas_id: 0,
89 next_qubit_id: 0,
90 next_qubit_hardware_id: HardwareId::default(),
91 qubit_map: IndexMap::new(),
92 instrs: String::new(),
93 measurements: String::new(),
94 decls: String::new(),
95 decl_names: FxHashSet::default(),
96 };
97 sim.instrs.push_str(include_str!("./qir_base/prefix.ll"));
98 sim
99 }
100
101 #[must_use]
102 pub fn finish(mut self, val: &Value) -> String {
103 self.instrs.push_str(&self.measurements);
104 self.write_output_recording(val)
105 .expect("writing to string should succeed");
106
107 write!(
108 self.instrs,
109 include_str!("./qir_base/postfix.ll"),
110 self.decls, self.next_qubit_hardware_id.0, self.next_meas_id
111 )
112 .expect("writing to string should succeed");
113
114 self.instrs
115 }
116
117 #[must_use]
118 fn get_meas_id(&mut self) -> usize {
119 let id = self.next_meas_id;
120 self.next_meas_id += 1;
121 id
122 }
123
124 fn map(&mut self, qubit: usize) -> HardwareId {
125 if let Some(mapped) = self.qubit_map.get(qubit) {
126 *mapped
127 } else {
128 let mapped = self.next_qubit_hardware_id;
129 self.next_qubit_hardware_id.0 += 1;
130 self.qubit_map.insert(qubit, mapped);
131 mapped
132 }
133 }
134
135 fn write_output_recording(&mut self, val: &Value) -> std::fmt::Result {
136 match val {
137 Value::Array(arr) => {
138 self.write_array_recording(arr.len())?;
139 for val in arr.iter() {
140 self.write_output_recording(val)?;
141 }
142 }
143 Value::Result(r) => {
144 self.write_result_recording(r.unwrap_id());
145 }
146 Value::Tuple(tup) => {
147 self.write_tuple_recording(tup.len())?;
148 for val in tup.iter() {
149 self.write_output_recording(val)?;
150 }
151 }
152 _ => panic!("unexpected value type: {val:?}"),
153 }
154 Ok(())
155 }
156
157 fn write_result_recording(&mut self, res: usize) {
158 writeln!(
159 self.instrs,
160 " call void @__quantum__rt__result_record_output({}, i8* null)",
161 Result(res),
162 )
163 .expect("writing to string should succeed");
164 }
165
166 fn write_tuple_recording(&mut self, size: usize) -> std::fmt::Result {
167 writeln!(
168 self.instrs,
169 " call void @__quantum__rt__tuple_record_output(i64 {size}, i8* null)"
170 )
171 }
172
173 fn write_array_recording(&mut self, size: usize) -> std::fmt::Result {
174 writeln!(
175 self.instrs,
176 " call void @__quantum__rt__array_record_output(i64 {size}, i8* null)"
177 )
178 }
179
180 fn write_arg(&mut self, arg: &Value) -> std::result::Result<(), String> {
181 match arg {
182 Value::Qubit(q) => {
183 let q = self.map(q.0);
184 write!(self.instrs, "{}", Qubit(q))
185 }
186 Value::Double(d) => write!(self.instrs, "{}", Double(*d)),
187 Value::Bool(b) => write!(self.instrs, "{}", Bool(*b)),
188 Value::Int(i) => write!(self.instrs, "{}", Int(*i)),
189 _ => return Err(format!("unsupported argument type: {}", arg.type_name())),
190 }
191 .expect("writing to string should succeed");
192 Ok(())
193 }
194
195 fn write_decl_type(&mut self, ty: &Value) -> std::result::Result<(), String> {
196 match ty {
197 Value::Qubit(_) => write!(self.decls, "%Qubit*"),
198 Value::Double(_) => write!(self.decls, "double"),
199 Value::Bool(_) => write!(self.decls, "i1"),
200 Value::Int(_) => write!(self.decls, "i64"),
201 _ => return Err(format!("unsupported argument type: {}", ty.type_name())),
202 }
203 .expect("writing to string should succeed");
204 Ok(())
205 }
206
207 fn write_decl(&mut self, name: &str, arg: &Value) -> std::result::Result<(), String> {
208 if self.decl_names.insert(name.to_string()) {
209 write!(self.decls, "declare void @{name}(").expect("writing to string should succeed");
210 if let Value::Tuple(args) = arg {
211 if let Some((first, rest)) = args.split_first() {
212 self.write_decl_type(first)?;
213 for arg in rest {
214 write!(self.decls, ", ").expect("writing to string should succeed");
215 self.write_decl_type(arg)?;
216 }
217 }
218 } else {
219 self.write_decl_type(arg)?;
220 }
221 writeln!(self.decls, ")").expect("writing to string should succeed");
222 }
223
224 Ok(())
225 }
226}
227
228impl Backend for BaseProfSim {
229 type ResultType = usize;
230
231 fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) {
232 let ctl0 = self.map(ctl0);
233 let ctl1 = self.map(ctl1);
234 let q = self.map(q);
235 writeln!(
236 self.instrs,
237 " call void @__quantum__qis__ccx__body({}, {}, {})",
238 Qubit(ctl0),
239 Qubit(ctl1),
240 Qubit(q)
241 )
242 .expect("writing to string should succeed");
243 }
244
245 fn cx(&mut self, ctl: usize, q: usize) {
246 let ctl = self.map(ctl);
247 let q = self.map(q);
248 writeln!(
249 self.instrs,
250 " call void @__quantum__qis__cx__body({}, {})",
251 Qubit(ctl),
252 Qubit(q),
253 )
254 .expect("writing to string should succeed");
255 }
256
257 fn cy(&mut self, ctl: usize, q: usize) {
258 let ctl = self.map(ctl);
259 let q = self.map(q);
260 writeln!(
261 self.instrs,
262 " call void @__quantum__qis__cy__body({}, {})",
263 Qubit(ctl),
264 Qubit(q),
265 )
266 .expect("writing to string should succeed");
267 }
268
269 fn cz(&mut self, ctl: usize, q: usize) {
270 let ctl = self.map(ctl);
271 let q = self.map(q);
272 writeln!(
273 self.instrs,
274 " call void @__quantum__qis__cz__body({}, {})",
275 Qubit(ctl),
276 Qubit(q),
277 )
278 .expect("writing to string should succeed");
279 }
280
281 fn h(&mut self, q: usize) {
282 let q = self.map(q);
283 writeln!(
284 self.instrs,
285 " call void @__quantum__qis__h__body({})",
286 Qubit(q),
287 )
288 .expect("writing to string should succeed");
289 }
290
291 fn m(&mut self, q: usize) -> Self::ResultType {
292 let mapped_q = self.map(q);
293 let id = self.get_meas_id();
294 // Measurements are tracked separately from instructions, so that they can be
295 // deferred until the end of the program.
296 writeln!(
297 self.measurements,
298 " call void @__quantum__qis__mz__body({}, {}) #1",
299 Qubit(mapped_q),
300 Result(id),
301 )
302 .expect("writing to string should succeed");
303 self.reset(q);
304 id
305 }
306
307 fn mresetz(&mut self, q: usize) -> Self::ResultType {
308 self.m(q)
309 }
310
311 fn reset(&mut self, q: usize) {
312 // Reset is a no-op in Base Profile, but does force qubit remapping so that future
313 // operations on the given qubit id are performed on a fresh qubit. Clear the entry in the map
314 // so it is known to require remapping on next use.
315 self.qubit_map.remove(q);
316 }
317
318 fn rx(&mut self, theta: f64, q: usize) {
319 let q = self.map(q);
320 writeln!(
321 self.instrs,
322 " call void @__quantum__qis__rx__body({}, {})",
323 Double(theta),
324 Qubit(q),
325 )
326 .expect("writing to string should succeed");
327 }
328
329 fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
330 let q0 = self.map(q0);
331 let q1 = self.map(q1);
332 writeln!(
333 self.instrs,
334 " call void @__quantum__qis__rxx__body({}, {}, {})",
335 Double(theta),
336 Qubit(q0),
337 Qubit(q1),
338 )
339 .expect("writing to string should succeed");
340 }
341
342 fn ry(&mut self, theta: f64, q: usize) {
343 let q = self.map(q);
344 writeln!(
345 self.instrs,
346 " call void @__quantum__qis__ry__body({}, {})",
347 Double(theta),
348 Qubit(q),
349 )
350 .expect("writing to string should succeed");
351 }
352
353 fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
354 let q0 = self.map(q0);
355 let q1 = self.map(q1);
356 writeln!(
357 self.instrs,
358 " call void @__quantum__qis__ryy__body({}, {}, {})",
359 Double(theta),
360 Qubit(q0),
361 Qubit(q1),
362 )
363 .expect("writing to string should succeed");
364 }
365
366 fn rz(&mut self, theta: f64, q: usize) {
367 let q = self.map(q);
368 writeln!(
369 self.instrs,
370 " call void @__quantum__qis__rz__body({}, {})",
371 Double(theta),
372 Qubit(q),
373 )
374 .expect("writing to string should succeed");
375 }
376
377 fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
378 let q0 = self.map(q0);
379 let q1 = self.map(q1);
380 writeln!(
381 self.instrs,
382 " call void @__quantum__qis__rzz__body({}, {}, {})",
383 Double(theta),
384 Qubit(q0),
385 Qubit(q1),
386 )
387 .expect("writing to string should succeed");
388 }
389
390 fn sadj(&mut self, q: usize) {
391 let q = self.map(q);
392 writeln!(
393 self.instrs,
394 " call void @__quantum__qis__s__adj({})",
395 Qubit(q),
396 )
397 .expect("writing to string should succeed");
398 }
399
400 fn s(&mut self, q: usize) {
401 let q = self.map(q);
402 writeln!(
403 self.instrs,
404 " call void @__quantum__qis__s__body({})",
405 Qubit(q),
406 )
407 .expect("writing to string should succeed");
408 }
409
410 fn swap(&mut self, q0: usize, q1: usize) {
411 let q0 = self.map(q0);
412 let q1 = self.map(q1);
413 writeln!(
414 self.instrs,
415 " call void @__quantum__qis__swap__body({}, {})",
416 Qubit(q0),
417 Qubit(q1),
418 )
419 .expect("writing to string should succeed");
420 }
421
422 fn tadj(&mut self, q: usize) {
423 let q = self.map(q);
424 writeln!(
425 self.instrs,
426 " call void @__quantum__qis__t__adj({})",
427 Qubit(q),
428 )
429 .expect("writing to string should succeed");
430 }
431
432 fn t(&mut self, q: usize) {
433 let q = self.map(q);
434 writeln!(
435 self.instrs,
436 " call void @__quantum__qis__t__body({})",
437 Qubit(q),
438 )
439 .expect("writing to string should succeed");
440 }
441
442 fn x(&mut self, q: usize) {
443 let q = self.map(q);
444 writeln!(
445 self.instrs,
446 " call void @__quantum__qis__x__body({})",
447 Qubit(q),
448 )
449 .expect("writing to string should succeed");
450 }
451
452 fn y(&mut self, q: usize) {
453 let q = self.map(q);
454 writeln!(
455 self.instrs,
456 " call void @__quantum__qis__y__body({})",
457 Qubit(q),
458 )
459 .expect("writing to string should succeed");
460 }
461
462 fn z(&mut self, q: usize) {
463 let q = self.map(q);
464 writeln!(
465 self.instrs,
466 " call void @__quantum__qis__z__body({})",
467 Qubit(q),
468 )
469 .expect("writing to string should succeed");
470 }
471
472 fn qubit_allocate(&mut self) -> usize {
473 let id = self.next_qubit_id;
474 self.next_qubit_id += 1;
475 let _ = self.map(id);
476 id
477 }
478
479 fn qubit_release(&mut self, _q: usize) {
480 self.next_qubit_id -= 1;
481 }
482
483 fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
484 (Vec::new(), 0)
485 }
486
487 fn qubit_is_zero(&mut self, _q: usize) -> bool {
488 // Because `qubit_is_zero` is called on every qubit release, this must return
489 // true to avoid a panic.
490 true
491 }
492
493 fn custom_intrinsic(
494 &mut self,
495 name: &str,
496 arg: Value,
497 ) -> Option<std::result::Result<Value, String>> {
498 match self.write_decl(name, &arg) {
499 Ok(()) => {}
500 Err(e) => return Some(Err(e)),
501 }
502 write!(self.instrs, " call void @{name}(").expect("writing to string should succeed");
503
504 if let Value::Tuple(args) = arg {
505 if let Some((first, rest)) = args.split_first() {
506 match self.write_arg(first) {
507 Ok(()) => {}
508 Err(e) => return Some(Err(e)),
509 }
510 for arg in rest {
511 write!(self.instrs, ", ").expect("writing to string should succeed");
512 match self.write_arg(arg) {
513 Ok(()) => {}
514 Err(e) => return Some(Err(e)),
515 }
516 }
517 }
518 } else {
519 match self.write_arg(&arg) {
520 Ok(()) => {}
521 Err(e) => return Some(Err(e)),
522 }
523 }
524
525 writeln!(self.instrs, ")").expect("writing to string should succeed");
526 Some(Ok(Value::unit()))
527 }
528}
529
530struct Qubit(HardwareId);
531
532impl Display for Qubit {
533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534 write!(f, "%Qubit* inttoptr (i64 {} to %Qubit*)", self.0 .0)
535 }
536}
537
538struct Result(usize);
539
540impl Display for Result {
541 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542 write!(f, "%Result* inttoptr (i64 {} to %Result*)", self.0)
543 }
544}
545
546struct Double(f64);
547
548impl Display for Double {
549 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
550 let v = self.0;
551 if (v.floor() - v.ceil()).abs() < f64::EPSILON {
552 // The value is a whole number, which requires at least one decimal point
553 // to differentiate it from an integer value.
554 write!(f, "double {v:.1}")
555 } else {
556 write!(f, "double {v}")
557 }
558 }
559}
560
561struct Bool(bool);
562
563impl Display for Bool {
564 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
565 if self.0 {
566 write!(f, "i1 true")
567 } else {
568 write!(f, "i1 false")
569 }
570 }
571}
572
573struct Int(i64);
574
575impl Display for Int {
576 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
577 write!(f, "i64 {}", self.0)
578 }
579}
580