microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ba70b613cd5d0f628daedebeb0809216134c1121

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir_base.rs

488lines · 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_expr, eval_stmt,
14 output::GenericReceiver,
15 val::{GlobalId, Value},
16 Env, Error, Global, NodeLookup, State,
17};
18use qsc_fir::fir::{BlockId, ExprId, ItemKind, PackageId, PatId, StmtId};
19use qsc_frontend::compile::PackageStore;
20use qsc_hir::hir::{self};
21use quantum_sparse_sim::QuantumSim;
22use std::fmt::{Display, Write};
23
24const PREFIX: &str = include_str!("./qir_base/prefix.ll");
25const POSTFIX: &str = include_str!("./qir_base/postfix.ll");
26
27/// # Errors
28///
29/// This function will return an error if execution was unable to complete.
30/// # Panics
31///
32/// This function will panic if compiler state is invalid or in out-of-memory conditions.
33pub fn generate_qir(
34 store: &PackageStore,
35 package: hir::PackageId,
36) -> std::result::Result<String, (Error, Vec<Frame>)> {
37 let mut fir_lowerer = qsc_eval::lower::Lowerer::new();
38 let mut fir_store = IndexMap::new();
39 let package = map_hir_package_to_fir(package);
40 let mut sim = BaseProfSim::default();
41 sim.instrs.push_str(PREFIX);
42
43 for (id, unit) in store.iter() {
44 fir_store.insert(
45 map_hir_package_to_fir(id),
46 fir_lowerer.lower_package(&unit.package),
47 );
48 }
49
50 let unit = fir_store.get(package).expect("store should have package");
51 let entry_expr = unit.entry.expect("package should have entry");
52
53 let mut stdout = std::io::sink();
54 let mut out = GenericReceiver::new(&mut stdout);
55 let result = eval_expr(
56 &mut State::new(package),
57 entry_expr,
58 &Lookup {
59 fir_store: &fir_store,
60 },
61 &mut Env::with_empty_scope(),
62 &mut sim,
63 &mut out,
64 );
65 match result {
66 Ok(val) => {
67 sim.write_output_recording(&val)
68 .expect("writing to string should succeed");
69 sim.instrs.push_str(POSTFIX);
70 Ok(sim.instrs)
71 }
72 Err((err, stack)) => Err((err, stack)),
73 }
74}
75
76/// # Errors
77/// This function will return an error if execution was unable to complete.
78/// # Panics
79///
80/// This function will panic if compiler state is invalid or in out-of-memory conditions.
81pub fn generate_qir_for_stmt(
82 stmt: StmtId,
83 globals: &impl NodeLookup,
84 env: &mut Env,
85 package: PackageId,
86) -> std::result::Result<String, (Error, Vec<Frame>)> {
87 let mut sim = BaseProfSim::default();
88 sim.instrs.push_str(PREFIX);
89 let mut stdout = std::io::sink();
90 let mut out = GenericReceiver::new(&mut stdout);
91 match eval_stmt(stmt, globals, env, &mut sim, package, &mut out) {
92 Ok(val) => {
93 sim.write_output_recording(&val)
94 .expect("writing to string should succeed");
95 sim.instrs.push_str(POSTFIX);
96 Ok(sim.instrs)
97 }
98 Err((err, stack)) => Err((err, stack)),
99 }
100}
101
102struct Lookup<'a> {
103 fir_store: &'a IndexMap<PackageId, qsc_fir::fir::Package>,
104}
105
106impl<'a> Lookup<'a> {
107 fn get_package(&self, package: PackageId) -> &qsc_fir::fir::Package {
108 self.fir_store
109 .get(package)
110 .expect("Package should be in FIR store")
111 }
112}
113
114impl<'a> NodeLookup for Lookup<'a> {
115 fn get(&self, id: GlobalId) -> Option<Global<'a>> {
116 get_global(self.fir_store, id)
117 }
118 fn get_block(&self, package: PackageId, id: BlockId) -> &qsc_fir::fir::Block {
119 self.get_package(package)
120 .blocks
121 .get(id)
122 .expect("BlockId should have been lowered")
123 }
124 fn get_expr(&self, package: PackageId, id: ExprId) -> &qsc_fir::fir::Expr {
125 self.get_package(package)
126 .exprs
127 .get(id)
128 .expect("ExprId should have been lowered")
129 }
130 fn get_pat(&self, package: PackageId, id: PatId) -> &qsc_fir::fir::Pat {
131 self.get_package(package)
132 .pats
133 .get(id)
134 .expect("PatId should have been lowered")
135 }
136 fn get_stmt(&self, package: PackageId, id: StmtId) -> &qsc_fir::fir::Stmt {
137 self.get_package(package)
138 .stmts
139 .get(id)
140 .expect("StmtId should have been lowered")
141 }
142}
143
144pub(super) fn get_global(
145 fir_store: &IndexMap<PackageId, qsc_fir::fir::Package>,
146 id: GlobalId,
147) -> Option<Global> {
148 fir_store
149 .get(id.package)
150 .and_then(|package| match &package.items.get(id.item)?.kind {
151 ItemKind::Callable(callable) => Some(Global::Callable(callable)),
152 ItemKind::Namespace(..) => None,
153 ItemKind::Ty(..) => Some(Global::Udt),
154 })
155}
156
157#[derive(Default)]
158struct BaseProfSim {
159 next_meas_id: usize,
160 sim: QuantumSim,
161 instrs: String,
162}
163
164impl BaseProfSim {
165 #[must_use]
166 fn get_meas_id(&mut self) -> usize {
167 let id = self.next_meas_id;
168 self.next_meas_id += 1;
169 id
170 }
171
172 fn write_output_recording(&mut self, val: &Value) -> std::fmt::Result {
173 match val {
174 Value::Array(arr) => {
175 self.write_array_recording(arr.len())?;
176 for val in arr.iter() {
177 self.write_output_recording(val)?;
178 }
179 }
180 Value::Result(r) => {
181 self.write_result_recording(r.unwrap_id());
182 }
183 Value::Tuple(tup) => {
184 self.write_tuple_recording(tup.len())?;
185 for val in tup.iter() {
186 self.write_output_recording(val)?;
187 }
188 }
189 _ => panic!("unexpected value type: {val:?}"),
190 }
191 Ok(())
192 }
193
194 fn write_result_recording(&mut self, res: usize) {
195 writeln!(
196 self.instrs,
197 " call void @__quantum__rt__result_record_output({}, i8* null)",
198 Result(res),
199 )
200 .expect("writing to string should succeed");
201 }
202
203 fn write_tuple_recording(&mut self, size: usize) -> std::fmt::Result {
204 writeln!(
205 self.instrs,
206 " call void @__quantum__rt__tuple_record_output(i64 {size}, i8* null)"
207 )
208 }
209
210 fn write_array_recording(&mut self, size: usize) -> std::fmt::Result {
211 writeln!(
212 self.instrs,
213 " call void @__quantum__rt__array_record_output(i64 {size}, i8* null)"
214 )
215 }
216}
217
218impl Backend for BaseProfSim {
219 type ResultType = usize;
220
221 fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) {
222 writeln!(
223 self.instrs,
224 " call void @__quantum__qis__ccx__body({}, {}, {})",
225 Qubit(ctl0),
226 Qubit(ctl1),
227 Qubit(q)
228 )
229 .expect("writing to string should succeed");
230 }
231
232 fn cx(&mut self, ctl: usize, q: usize) {
233 writeln!(
234 self.instrs,
235 " call void @__quantum__qis__cx__body({}, {})",
236 Qubit(ctl),
237 Qubit(q),
238 )
239 .expect("writing to string should succeed");
240 }
241
242 fn cy(&mut self, ctl: usize, q: usize) {
243 writeln!(
244 self.instrs,
245 " call void @__quantum__qis__cy__body({}, {})",
246 Qubit(ctl),
247 Qubit(q),
248 )
249 .expect("writing to string should succeed");
250 }
251
252 fn cz(&mut self, ctl: usize, q: usize) {
253 writeln!(
254 self.instrs,
255 " call void @__quantum__qis__cz__body({}, {})",
256 Qubit(ctl),
257 Qubit(q),
258 )
259 .expect("writing to string should succeed");
260 }
261
262 fn h(&mut self, q: usize) {
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 let id = self.get_meas_id();
273 writeln!(
274 self.instrs,
275 " call void @__quantum__qis__m__body({}, {}) #1",
276 Qubit(q),
277 Result(id),
278 )
279 .expect("writing to string should succeed");
280 id
281 }
282
283 fn mresetz(&mut self, q: usize) -> Self::ResultType {
284 let id = self.get_meas_id();
285 writeln!(
286 self.instrs,
287 " call void @__quantum__qis__mresetz__body({}, {}) #1",
288 Qubit(q),
289 Result(id),
290 )
291 .expect("writing to string should succeed");
292 id
293 }
294
295 fn reset(&mut self, q: usize) {
296 writeln!(
297 self.instrs,
298 " call void @__quantum__qis__reset__body({})",
299 Qubit(q),
300 )
301 .expect("writing to string should succeed");
302 }
303
304 fn rx(&mut self, theta: f64, q: usize) {
305 writeln!(
306 self.instrs,
307 " call void @__quantum__qis__rx__body({}, {})",
308 Double(theta),
309 Qubit(q),
310 )
311 .expect("writing to string should succeed");
312 }
313
314 fn rxx(&mut self, theta: f64, q0: usize, q1: usize) {
315 writeln!(
316 self.instrs,
317 " call void @__quantum__qis__rxx__body({}, {}, {})",
318 Double(theta),
319 Qubit(q0),
320 Qubit(q1),
321 )
322 .expect("writing to string should succeed");
323 }
324
325 fn ry(&mut self, theta: f64, q: usize) {
326 writeln!(
327 self.instrs,
328 " call void @__quantum__qis__ry__body({}, {})",
329 Double(theta),
330 Qubit(q),
331 )
332 .expect("writing to string should succeed");
333 }
334
335 fn ryy(&mut self, theta: f64, q0: usize, q1: usize) {
336 writeln!(
337 self.instrs,
338 " call void @__quantum__qis__ryy__body({}, {}, {})",
339 Double(theta),
340 Qubit(q0),
341 Qubit(q1),
342 )
343 .expect("writing to string should succeed");
344 }
345
346 fn rz(&mut self, theta: f64, q: usize) {
347 writeln!(
348 self.instrs,
349 " call void @__quantum__qis__rz__body({}, {})",
350 Double(theta),
351 Qubit(q),
352 )
353 .expect("writing to string should succeed");
354 }
355
356 fn rzz(&mut self, theta: f64, q0: usize, q1: usize) {
357 writeln!(
358 self.instrs,
359 " call void @__quantum__qis__rzz__body({}, {}, {})",
360 Double(theta),
361 Qubit(q0),
362 Qubit(q1),
363 )
364 .expect("writing to string should succeed");
365 }
366
367 fn sadj(&mut self, q: usize) {
368 writeln!(
369 self.instrs,
370 " call void @__quantum__qis__s__adj({})",
371 Qubit(q),
372 )
373 .expect("writing to string should succeed");
374 }
375
376 fn s(&mut self, q: usize) {
377 writeln!(
378 self.instrs,
379 " call void @__quantum__qis__s__body({})",
380 Qubit(q),
381 )
382 .expect("writing to string should succeed");
383 }
384
385 fn swap(&mut self, q0: usize, q1: usize) {
386 writeln!(
387 self.instrs,
388 " call void @__quantum__qis__swap__body({}, {})",
389 Qubit(q0),
390 Qubit(q1),
391 )
392 .expect("writing to string should succeed");
393 }
394
395 fn tadj(&mut self, q: usize) {
396 writeln!(
397 self.instrs,
398 " call void @__quantum__qis__t__adj({})",
399 Qubit(q),
400 )
401 .expect("writing to string should succeed");
402 }
403
404 fn t(&mut self, q: usize) {
405 writeln!(
406 self.instrs,
407 " call void @__quantum__qis__t__body({})",
408 Qubit(q),
409 )
410 .expect("writing to string should succeed");
411 }
412
413 fn x(&mut self, q: usize) {
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 writeln!(
424 self.instrs,
425 " call void @__quantum__qis__y__body({})",
426 Qubit(q),
427 )
428 .expect("writing to string should succeed");
429 }
430
431 fn z(&mut self, q: usize) {
432 writeln!(
433 self.instrs,
434 " call void @__quantum__qis__z__body({})",
435 Qubit(q),
436 )
437 .expect("writing to string should succeed");
438 }
439
440 fn qubit_allocate(&mut self) -> usize {
441 self.sim.allocate()
442 }
443
444 fn qubit_release(&mut self, q: usize) {
445 self.sim.release(q);
446 }
447
448 fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
449 (Vec::new(), 0)
450 }
451
452 fn qubit_is_zero(&mut self, _q: usize) -> bool {
453 // Because `qubit_is_zero` is called on every qubit release, this must return
454 // true to avoid a panic.
455 true
456 }
457}
458
459struct Qubit(usize);
460
461impl Display for Qubit {
462 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
463 write!(f, "%Qubit* inttoptr (i64 {} to %Qubit*)", self.0)
464 }
465}
466
467struct Result(usize);
468
469impl Display for Result {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 write!(f, "%Result* inttoptr (i64 {} to %Result*)", self.0)
472 }
473}
474
475struct Double(f64);
476
477impl Display for Double {
478 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
479 let v = self.0;
480 if (v.floor() - v.ceil()).abs() < f64::EPSILON {
481 // The value is a whole number, which requires at least one decimal point
482 // to differentiate it from an integer value.
483 write!(f, "double {v:.1}")
484 } else {
485 write!(f, "double {v}")
486 }
487 }
488}
489