microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
cesarzc/ssa-panic

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir_base.rs

552lines · modecode

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