microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-2145

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir.rs

759lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod instruction_tests;
6
7#[cfg(test)]
8mod tests;
9
10use qsc_data_structures::target::TargetCapabilityFlags;
11use qsc_eval::val::Value;
12use qsc_lowerer::map_hir_package_to_fir;
13use qsc_partial_eval::{partially_evaluate, partially_evaluate_call, ProgramEntry};
14use qsc_rca::PackageStoreComputeProperties;
15use qsc_rir::{
16 passes::check_and_transform,
17 rir::{self, ConditionCode, FcmpConditionCode, Program},
18 utils::get_all_block_successors,
19};
20use std::fmt::Write;
21
22fn lower_store(package_store: &qsc_frontend::compile::PackageStore) -> qsc_fir::fir::PackageStore {
23 let mut fir_store = qsc_fir::fir::PackageStore::new();
24 for (id, unit) in package_store {
25 let package = qsc_lowerer::Lowerer::new().lower_package(&unit.package, &fir_store);
26 fir_store.insert(map_hir_package_to_fir(id), package);
27 }
28 fir_store
29}
30
31/// converts the given sources to QIR using the given language features.
32pub fn hir_to_qir(
33 package_store: &qsc_frontend::compile::PackageStore,
34 capabilities: TargetCapabilityFlags,
35 compute_properties: Option<PackageStoreComputeProperties>,
36 entry: &ProgramEntry,
37) -> Result<String, qsc_partial_eval::Error> {
38 let fir_store = lower_store(package_store);
39 fir_to_qir(&fir_store, capabilities, compute_properties, entry)
40}
41
42/// converts the given sources to RIR using the given language features.
43pub fn fir_to_rir(
44 fir_store: &qsc_fir::fir::PackageStore,
45 capabilities: TargetCapabilityFlags,
46 compute_properties: Option<PackageStoreComputeProperties>,
47 entry: &ProgramEntry,
48) -> Result<(Program, Program), qsc_partial_eval::Error> {
49 let mut program = get_rir_from_compilation(fir_store, compute_properties, entry, capabilities)?;
50 let orig = program.clone();
51 check_and_transform(&mut program);
52 Ok((orig, program))
53}
54
55/// converts the given sources to QIR using the given language features.
56pub fn fir_to_qir(
57 fir_store: &qsc_fir::fir::PackageStore,
58 capabilities: TargetCapabilityFlags,
59 compute_properties: Option<PackageStoreComputeProperties>,
60 entry: &ProgramEntry,
61) -> Result<String, qsc_partial_eval::Error> {
62 let mut program = get_rir_from_compilation(fir_store, compute_properties, entry, capabilities)?;
63 check_and_transform(&mut program);
64 Ok(ToQir::<String>::to_qir(&program, &program))
65}
66
67/// converts the given callable to QIR using the given arguments and language features.
68pub fn fir_to_qir_from_callable(
69 fir_store: &qsc_fir::fir::PackageStore,
70 capabilities: TargetCapabilityFlags,
71 compute_properties: Option<PackageStoreComputeProperties>,
72 callable: qsc_fir::fir::StoreItemId,
73 args: Value,
74) -> Result<String, qsc_partial_eval::Error> {
75 let compute_properties = compute_properties.unwrap_or_else(|| {
76 let analyzer = qsc_rca::Analyzer::init(fir_store);
77 analyzer.analyze_all()
78 });
79
80 let mut program =
81 partially_evaluate_call(fir_store, &compute_properties, callable, args, capabilities)?;
82 check_and_transform(&mut program);
83 Ok(ToQir::<String>::to_qir(&program, &program))
84}
85
86fn get_rir_from_compilation(
87 fir_store: &qsc_fir::fir::PackageStore,
88 compute_properties: Option<PackageStoreComputeProperties>,
89 entry: &ProgramEntry,
90 capabilities: TargetCapabilityFlags,
91) -> Result<rir::Program, qsc_partial_eval::Error> {
92 let compute_properties = compute_properties.unwrap_or_else(|| {
93 let analyzer = qsc_rca::Analyzer::init(fir_store);
94 analyzer.analyze_all()
95 });
96
97 partially_evaluate(fir_store, &compute_properties, entry, capabilities)
98}
99
100/// A trait for converting a type into QIR of type `T`.
101/// This can be used to generate QIR strings or other representations.
102pub trait ToQir<T> {
103 fn to_qir(&self, program: &rir::Program) -> T;
104}
105
106impl ToQir<String> for rir::Literal {
107 fn to_qir(&self, _program: &rir::Program) -> String {
108 match self {
109 rir::Literal::Bool(b) => format!("i1 {b}"),
110 rir::Literal::Double(d) => {
111 if (d.floor() - d.ceil()).abs() < f64::EPSILON {
112 // The value is a whole number, which requires at least one decimal point
113 // to differentiate it from an integer value.
114 format!("double {d:.1}")
115 } else {
116 format!("double {d}")
117 }
118 }
119 rir::Literal::Integer(i) => format!("i64 {i}"),
120 rir::Literal::Pointer => "i8* null".to_string(),
121 rir::Literal::Qubit(q) => format!("%Qubit* inttoptr (i64 {q} to %Qubit*)"),
122 rir::Literal::Result(r) => format!("%Result* inttoptr (i64 {r} to %Result*)"),
123 }
124 }
125}
126
127impl ToQir<String> for rir::Ty {
128 fn to_qir(&self, _program: &rir::Program) -> String {
129 match self {
130 rir::Ty::Boolean => "i1".to_string(),
131 rir::Ty::Double => "double".to_string(),
132 rir::Ty::Integer => "i64".to_string(),
133 rir::Ty::Pointer => "i8*".to_string(),
134 rir::Ty::Qubit => "%Qubit*".to_string(),
135 rir::Ty::Result => "%Result*".to_string(),
136 }
137 }
138}
139
140impl ToQir<String> for Option<rir::Ty> {
141 fn to_qir(&self, program: &rir::Program) -> String {
142 match self {
143 Some(ty) => ToQir::<String>::to_qir(ty, program),
144 None => "void".to_string(),
145 }
146 }
147}
148
149impl ToQir<String> for rir::VariableId {
150 fn to_qir(&self, _program: &rir::Program) -> String {
151 format!("%var_{}", self.0)
152 }
153}
154
155impl ToQir<String> for rir::Variable {
156 fn to_qir(&self, program: &rir::Program) -> String {
157 format!(
158 "{} {}",
159 ToQir::<String>::to_qir(&self.ty, program),
160 ToQir::<String>::to_qir(&self.variable_id, program)
161 )
162 }
163}
164
165impl ToQir<String> for rir::Operand {
166 fn to_qir(&self, program: &rir::Program) -> String {
167 match self {
168 rir::Operand::Literal(lit) => ToQir::<String>::to_qir(lit, program),
169 rir::Operand::Variable(var) => ToQir::<String>::to_qir(var, program),
170 }
171 }
172}
173
174impl ToQir<String> for rir::FcmpConditionCode {
175 fn to_qir(&self, _program: &rir::Program) -> String {
176 match self {
177 rir::FcmpConditionCode::False => "false".to_string(),
178 rir::FcmpConditionCode::OrderedAndEqual => "oeq".to_string(),
179 rir::FcmpConditionCode::OrderedAndGreaterThan => "ogt".to_string(),
180 rir::FcmpConditionCode::OrderedAndGreaterThanOrEqual => "oge".to_string(),
181 rir::FcmpConditionCode::OrderedAndLessThan => "olt".to_string(),
182 rir::FcmpConditionCode::OrderedAndLessThanOrEqual => "ole".to_string(),
183 rir::FcmpConditionCode::OrderedAndNotEqual => "one".to_string(),
184 rir::FcmpConditionCode::Ordered => "ord".to_string(),
185 rir::FcmpConditionCode::UnorderedOrEqual => "ueq".to_string(),
186 rir::FcmpConditionCode::UnorderedOrGreaterThan => "ugt".to_string(),
187 rir::FcmpConditionCode::UnorderedOrGreaterThanOrEqual => "uge".to_string(),
188 rir::FcmpConditionCode::UnorderedOrLessThan => "ult".to_string(),
189 rir::FcmpConditionCode::UnorderedOrLessThanOrEqual => "ule".to_string(),
190 rir::FcmpConditionCode::UnorderedOrNotEqual => "une".to_string(),
191 rir::FcmpConditionCode::Unordered => "uno".to_string(),
192 rir::FcmpConditionCode::True => "true".to_string(),
193 }
194 }
195}
196
197impl ToQir<String> for rir::ConditionCode {
198 fn to_qir(&self, _program: &rir::Program) -> String {
199 match self {
200 rir::ConditionCode::Eq => "eq".to_string(),
201 rir::ConditionCode::Ne => "ne".to_string(),
202 rir::ConditionCode::Sgt => "sgt".to_string(),
203 rir::ConditionCode::Sge => "sge".to_string(),
204 rir::ConditionCode::Slt => "slt".to_string(),
205 rir::ConditionCode::Sle => "sle".to_string(),
206 }
207 }
208}
209
210impl ToQir<String> for rir::Instruction {
211 fn to_qir(&self, program: &rir::Program) -> String {
212 match self {
213 rir::Instruction::Add(lhs, rhs, variable) => {
214 binop_to_qir("add", lhs, rhs, *variable, program)
215 }
216 rir::Instruction::Ashr(lhs, rhs, variable) => {
217 binop_to_qir("ashr", lhs, rhs, *variable, program)
218 }
219 rir::Instruction::BitwiseAnd(lhs, rhs, variable) => {
220 simple_bitwise_to_qir("and", lhs, rhs, *variable, program)
221 }
222 rir::Instruction::BitwiseNot(value, variable) => {
223 bitwise_not_to_qir(value, *variable, program)
224 }
225 rir::Instruction::BitwiseOr(lhs, rhs, variable) => {
226 simple_bitwise_to_qir("or", lhs, rhs, *variable, program)
227 }
228 rir::Instruction::BitwiseXor(lhs, rhs, variable) => {
229 simple_bitwise_to_qir("xor", lhs, rhs, *variable, program)
230 }
231 rir::Instruction::Branch(cond, true_id, false_id) => {
232 format!(
233 " br {}, label %{}, label %{}",
234 ToQir::<String>::to_qir(cond, program),
235 ToQir::<String>::to_qir(true_id, program),
236 ToQir::<String>::to_qir(false_id, program)
237 )
238 }
239 rir::Instruction::Call(call_id, args, output) => {
240 call_to_qir(args, *call_id, *output, program)
241 }
242 rir::Instruction::Fadd(lhs, rhs, variable) => {
243 fbinop_to_qir("fadd", lhs, rhs, *variable, program)
244 }
245 rir::Instruction::Fdiv(lhs, rhs, variable) => {
246 fbinop_to_qir("fdiv", lhs, rhs, *variable, program)
247 }
248 rir::Instruction::Fmul(lhs, rhs, variable) => {
249 fbinop_to_qir("fmul", lhs, rhs, *variable, program)
250 }
251 rir::Instruction::Fsub(lhs, rhs, variable) => {
252 fbinop_to_qir("fsub", lhs, rhs, *variable, program)
253 }
254 rir::Instruction::LogicalAnd(lhs, rhs, variable) => {
255 logical_binop_to_qir("and", lhs, rhs, *variable, program)
256 }
257 rir::Instruction::LogicalNot(value, variable) => {
258 logical_not_to_qir(value, *variable, program)
259 }
260 rir::Instruction::LogicalOr(lhs, rhs, variable) => {
261 logical_binop_to_qir("or", lhs, rhs, *variable, program)
262 }
263 rir::Instruction::Mul(lhs, rhs, variable) => {
264 binop_to_qir("mul", lhs, rhs, *variable, program)
265 }
266 rir::Instruction::Fcmp(op, lhs, rhs, variable) => {
267 fcmp_to_qir(*op, lhs, rhs, *variable, program)
268 }
269 rir::Instruction::Icmp(op, lhs, rhs, variable) => {
270 icmp_to_qir(*op, lhs, rhs, *variable, program)
271 }
272 rir::Instruction::Jump(block_id) => {
273 format!(" br label %{}", ToQir::<String>::to_qir(block_id, program))
274 }
275 rir::Instruction::Phi(args, variable) => phi_to_qir(args, *variable, program),
276 rir::Instruction::Return => " ret void".to_string(),
277 rir::Instruction::Sdiv(lhs, rhs, variable) => {
278 binop_to_qir("sdiv", lhs, rhs, *variable, program)
279 }
280 rir::Instruction::Shl(lhs, rhs, variable) => {
281 binop_to_qir("shl", lhs, rhs, *variable, program)
282 }
283 rir::Instruction::Srem(lhs, rhs, variable) => {
284 binop_to_qir("srem", lhs, rhs, *variable, program)
285 }
286 rir::Instruction::Store(_, _) => unimplemented!("store should be removed by pass"),
287 rir::Instruction::Sub(lhs, rhs, variable) => {
288 binop_to_qir("sub", lhs, rhs, *variable, program)
289 }
290 }
291 }
292}
293
294fn logical_not_to_qir(
295 value: &rir::Operand,
296 variable: rir::Variable,
297 program: &rir::Program,
298) -> String {
299 let value_ty = get_value_ty(value);
300 let var_ty = get_variable_ty(variable);
301 assert_eq!(
302 value_ty, var_ty,
303 "mismatched input/output types ({value_ty}, {var_ty}) for not"
304 );
305 assert_eq!(var_ty, "i1", "unsupported type {var_ty} for not");
306
307 format!(
308 " {} = xor i1 {}, true",
309 ToQir::<String>::to_qir(&variable.variable_id, program),
310 get_value_as_str(value, program)
311 )
312}
313
314fn logical_binop_to_qir(
315 op: &str,
316 lhs: &rir::Operand,
317 rhs: &rir::Operand,
318 variable: rir::Variable,
319 program: &rir::Program,
320) -> String {
321 let lhs_ty = get_value_ty(lhs);
322 let rhs_ty = get_value_ty(rhs);
323 let var_ty = get_variable_ty(variable);
324 assert_eq!(
325 lhs_ty, rhs_ty,
326 "mismatched input types ({lhs_ty}, {rhs_ty}) for {op}"
327 );
328 assert_eq!(
329 lhs_ty, var_ty,
330 "mismatched input/output types ({lhs_ty}, {var_ty}) for {op}"
331 );
332 assert_eq!(var_ty, "i1", "unsupported type {var_ty} for {op}");
333
334 format!(
335 " {} = {op} {var_ty} {}, {}",
336 ToQir::<String>::to_qir(&variable.variable_id, program),
337 get_value_as_str(lhs, program),
338 get_value_as_str(rhs, program)
339 )
340}
341
342fn bitwise_not_to_qir(
343 value: &rir::Operand,
344 variable: rir::Variable,
345 program: &rir::Program,
346) -> String {
347 let value_ty = get_value_ty(value);
348 let var_ty = get_variable_ty(variable);
349 assert_eq!(
350 value_ty, var_ty,
351 "mismatched input/output types ({value_ty}, {var_ty}) for not"
352 );
353 assert_eq!(var_ty, "i64", "unsupported type {var_ty} for not");
354
355 format!(
356 " {} = xor {var_ty} {}, -1",
357 ToQir::<String>::to_qir(&variable.variable_id, program),
358 get_value_as_str(value, program)
359 )
360}
361
362fn call_to_qir(
363 args: &[rir::Operand],
364 call_id: rir::CallableId,
365 output: Option<rir::Variable>,
366 program: &rir::Program,
367) -> String {
368 let args = args
369 .iter()
370 .map(|arg| ToQir::<String>::to_qir(arg, program))
371 .collect::<Vec<_>>()
372 .join(", ");
373 let callable = program.get_callable(call_id);
374 if let Some(output) = output {
375 format!(
376 " {} = call {} @{}({args})",
377 ToQir::<String>::to_qir(&output.variable_id, program),
378 ToQir::<String>::to_qir(&callable.output_type, program),
379 callable.name
380 )
381 } else {
382 format!(
383 " call {} @{}({args})",
384 ToQir::<String>::to_qir(&callable.output_type, program),
385 callable.name
386 )
387 }
388}
389
390fn fcmp_to_qir(
391 op: FcmpConditionCode,
392 lhs: &rir::Operand,
393 rhs: &rir::Operand,
394 variable: rir::Variable,
395 program: &rir::Program,
396) -> String {
397 let lhs_ty = get_value_ty(lhs);
398 let rhs_ty = get_value_ty(rhs);
399 let var_ty = get_variable_ty(variable);
400 assert_eq!(
401 lhs_ty, rhs_ty,
402 "mismatched input types ({lhs_ty}, {rhs_ty}) for fcmp {op}"
403 );
404
405 assert_eq!(var_ty, "i1", "unsupported output type {var_ty} for fcmp");
406 format!(
407 " {} = fcmp {} {lhs_ty} {}, {}",
408 ToQir::<String>::to_qir(&variable.variable_id, program),
409 ToQir::<String>::to_qir(&op, program),
410 get_value_as_str(lhs, program),
411 get_value_as_str(rhs, program)
412 )
413}
414
415fn icmp_to_qir(
416 op: ConditionCode,
417 lhs: &rir::Operand,
418 rhs: &rir::Operand,
419 variable: rir::Variable,
420 program: &rir::Program,
421) -> String {
422 let lhs_ty = get_value_ty(lhs);
423 let rhs_ty = get_value_ty(rhs);
424 let var_ty = get_variable_ty(variable);
425 assert_eq!(
426 lhs_ty, rhs_ty,
427 "mismatched input types ({lhs_ty}, {rhs_ty}) for icmp {op}"
428 );
429
430 assert_eq!(var_ty, "i1", "unsupported output type {var_ty} for icmp");
431 format!(
432 " {} = icmp {} {lhs_ty} {}, {}",
433 ToQir::<String>::to_qir(&variable.variable_id, program),
434 ToQir::<String>::to_qir(&op, program),
435 get_value_as_str(lhs, program),
436 get_value_as_str(rhs, program)
437 )
438}
439
440fn binop_to_qir(
441 op: &str,
442 lhs: &rir::Operand,
443 rhs: &rir::Operand,
444 variable: rir::Variable,
445 program: &rir::Program,
446) -> String {
447 let lhs_ty = get_value_ty(lhs);
448 let rhs_ty = get_value_ty(rhs);
449 let var_ty = get_variable_ty(variable);
450 assert_eq!(
451 lhs_ty, rhs_ty,
452 "mismatched input types ({lhs_ty}, {rhs_ty}) for {op}"
453 );
454 assert_eq!(
455 lhs_ty, var_ty,
456 "mismatched input/output types ({lhs_ty}, {var_ty}) for {op}"
457 );
458 assert_eq!(var_ty, "i64", "unsupported type {var_ty} for {op}");
459
460 format!(
461 " {} = {op} {var_ty} {}, {}",
462 ToQir::<String>::to_qir(&variable.variable_id, program),
463 get_value_as_str(lhs, program),
464 get_value_as_str(rhs, program)
465 )
466}
467
468fn fbinop_to_qir(
469 op: &str,
470 lhs: &rir::Operand,
471 rhs: &rir::Operand,
472 variable: rir::Variable,
473 program: &rir::Program,
474) -> String {
475 let lhs_ty = get_value_ty(lhs);
476 let rhs_ty = get_value_ty(rhs);
477 let var_ty = get_variable_ty(variable);
478 assert_eq!(
479 lhs_ty, rhs_ty,
480 "mismatched input types ({lhs_ty}, {rhs_ty}) for {op}"
481 );
482 assert_eq!(
483 lhs_ty, var_ty,
484 "mismatched input/output types ({lhs_ty}, {var_ty}) for {op}"
485 );
486 assert_eq!(var_ty, "double", "unsupported type {var_ty} for {op}");
487
488 format!(
489 " {} = {op} {var_ty} {}, {}",
490 ToQir::<String>::to_qir(&variable.variable_id, program),
491 get_value_as_str(lhs, program),
492 get_value_as_str(rhs, program)
493 )
494}
495
496fn simple_bitwise_to_qir(
497 op: &str,
498 lhs: &rir::Operand,
499 rhs: &rir::Operand,
500 variable: rir::Variable,
501 program: &rir::Program,
502) -> String {
503 let lhs_ty = get_value_ty(lhs);
504 let rhs_ty = get_value_ty(rhs);
505 let var_ty = get_variable_ty(variable);
506 assert_eq!(
507 lhs_ty, rhs_ty,
508 "mismatched input types ({lhs_ty}, {rhs_ty}) for {op}"
509 );
510 assert_eq!(
511 lhs_ty, var_ty,
512 "mismatched input/output types ({lhs_ty}, {var_ty}) for {op}"
513 );
514 assert_eq!(var_ty, "i64", "unsupported type {var_ty} for {op}");
515
516 format!(
517 " {} = {op} {var_ty} {}, {}",
518 ToQir::<String>::to_qir(&variable.variable_id, program),
519 get_value_as_str(lhs, program),
520 get_value_as_str(rhs, program)
521 )
522}
523
524fn phi_to_qir(
525 args: &[(rir::Operand, rir::BlockId)],
526 variable: rir::Variable,
527 program: &rir::Program,
528) -> String {
529 assert!(
530 !args.is_empty(),
531 "phi instruction should have at least one argument"
532 );
533 let var_ty = get_variable_ty(variable);
534 let args = args
535 .iter()
536 .map(|(arg, block_id)| {
537 let arg_ty = get_value_ty(arg);
538 assert_eq!(
539 arg_ty, var_ty,
540 "mismatched types ({var_ty} [... {arg_ty}]) for phi"
541 );
542 format!(
543 "[{}, %{}]",
544 get_value_as_str(arg, program),
545 ToQir::<String>::to_qir(block_id, program)
546 )
547 })
548 .collect::<Vec<_>>()
549 .join(", ");
550
551 format!(
552 " {} = phi {var_ty} {args}",
553 ToQir::<String>::to_qir(&variable.variable_id, program)
554 )
555}
556
557fn get_value_as_str(value: &rir::Operand, program: &rir::Program) -> String {
558 match value {
559 rir::Operand::Literal(lit) => match lit {
560 rir::Literal::Bool(b) => format!("{b}"),
561 rir::Literal::Double(d) => {
562 if (d.floor() - d.ceil()).abs() < f64::EPSILON {
563 // The value is a whole number, which requires at least one decimal point
564 // to differentiate it from an integer value.
565 format!("{d:.1}")
566 } else {
567 format!("{d}")
568 }
569 }
570 rir::Literal::Integer(i) => format!("{i}"),
571 rir::Literal::Pointer => "null".to_string(),
572 rir::Literal::Qubit(q) => format!("{q}"),
573 rir::Literal::Result(r) => format!("{r}"),
574 },
575 rir::Operand::Variable(var) => ToQir::<String>::to_qir(&var.variable_id, program),
576 }
577}
578
579fn get_value_ty(lhs: &rir::Operand) -> &str {
580 match lhs {
581 rir::Operand::Literal(lit) => match lit {
582 rir::Literal::Integer(_) => "i64",
583 rir::Literal::Bool(_) => "i1",
584 rir::Literal::Double(_) => get_f64_ty(),
585 rir::Literal::Qubit(_) => "%Qubit*",
586 rir::Literal::Result(_) => "%Result*",
587 rir::Literal::Pointer => "i8*",
588 },
589 rir::Operand::Variable(var) => get_variable_ty(*var),
590 }
591}
592
593fn get_variable_ty(variable: rir::Variable) -> &'static str {
594 match variable.ty {
595 rir::Ty::Integer => "i64",
596 rir::Ty::Boolean => "i1",
597 rir::Ty::Double => get_f64_ty(),
598 rir::Ty::Qubit => "%Qubit*",
599 rir::Ty::Result => "%Result*",
600 rir::Ty::Pointer => "i8*",
601 }
602}
603
604/// phi only supports "Floating-Point Types" which are defined as:
605/// - `half` (`f16`)
606/// - `bfloat`
607/// - `float` (`f32`)
608/// - `double` (`f64`)
609/// - `fp128`
610///
611/// We only support `f64`, so we break the pattern used for integers
612/// and have to use `double` here.
613///
614/// This conflicts with the QIR spec which says f64. Need to follow up on this.
615fn get_f64_ty() -> &'static str {
616 "double"
617}
618
619impl ToQir<String> for rir::BlockId {
620 fn to_qir(&self, _program: &rir::Program) -> String {
621 format!("block_{}", self.0)
622 }
623}
624
625impl ToQir<String> for rir::Block {
626 fn to_qir(&self, program: &rir::Program) -> String {
627 self.0
628 .iter()
629 .map(|instr| ToQir::<String>::to_qir(instr, program))
630 .collect::<Vec<_>>()
631 .join("\n")
632 }
633}
634
635impl ToQir<String> for rir::Callable {
636 fn to_qir(&self, program: &rir::Program) -> String {
637 let input_type = self
638 .input_type
639 .iter()
640 .map(|t| ToQir::<String>::to_qir(t, program))
641 .collect::<Vec<_>>()
642 .join(", ");
643 let output_type = ToQir::<String>::to_qir(&self.output_type, program);
644 let Some(entry_id) = self.body else {
645 return format!(
646 "declare {output_type} @{}({input_type}){}",
647 self.name,
648 if matches!(
649 self.call_type,
650 rir::CallableType::Measurement | rir::CallableType::Reset
651 ) {
652 // These callables are a special case that need the irreversable attribute.
653 " #1"
654 } else {
655 ""
656 }
657 );
658 };
659 let mut body = String::new();
660 let mut all_blocks = vec![entry_id];
661 all_blocks.extend(get_all_block_successors(entry_id, program));
662 for block_id in all_blocks {
663 let block = program.get_block(block_id);
664 write!(
665 body,
666 "{}:\n{}\n",
667 ToQir::<String>::to_qir(&block_id, program),
668 ToQir::<String>::to_qir(block, program)
669 )
670 .expect("writing to string should succeed");
671 }
672 assert!(
673 input_type.is_empty(),
674 "entry point should not have an input"
675 );
676 format!("define {output_type} @ENTRYPOINT__main() #0 {{\n{body}}}",)
677 }
678}
679
680impl ToQir<String> for rir::Program {
681 fn to_qir(&self, _program: &rir::Program) -> String {
682 let callables = self
683 .callables
684 .iter()
685 .map(|(_, callable)| ToQir::<String>::to_qir(callable, self))
686 .collect::<Vec<_>>()
687 .join("\n\n");
688 let profile = if self.config.is_base() {
689 "base_profile"
690 } else {
691 "adaptive_profile"
692 };
693 let body = format!(
694 include_str!("./qir/template.ll"),
695 callables, profile, self.num_qubits, self.num_results
696 );
697 let flags = get_module_metadata(self);
698 body + "\n" + &flags
699 }
700}
701
702/// Create the module metadata for the given program.
703/// creating the `llvm.module.flags` and its associated values.
704fn get_module_metadata(program: &rir::Program) -> String {
705 let mut flags = String::new();
706
707 // push the default attrs, we don't have any config values
708 // for now that would change any of them.
709 flags.push_str(
710 r#"
711!0 = !{i32 1, !"qir_major_version", i32 1}
712!1 = !{i32 7, !"qir_minor_version", i32 0}
713!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
714!3 = !{i32 1, !"dynamic_result_management", i1 false}
715"#,
716 );
717
718 let mut index = 4;
719
720 // If we are not in the base profile, we need to add the capabilities
721 // associated with the adaptive profile.
722 if !program.config.is_base() {
723 // loop through the capabilities and add them to the metadata
724 // for values that we can generate.
725 for cap in program.config.capabilities.iter() {
726 match cap {
727 TargetCapabilityFlags::IntegerComputations => {
728 let name = "int_computations";
729 writeln!(
730 flags,
731 "!{} = !{{i32 {}, !\"{}\", !\"i{}\"}}",
732 index, 1, name, 64
733 )
734 .expect("writing to string should succeed");
735 index += 1;
736 }
737 TargetCapabilityFlags::FloatingPointComputations => {
738 let name = "float_computations";
739 writeln!(
740 flags,
741 "!{} = !{{i32 {}, !\"{}\", !\"f{}\"}}",
742 index, 1, name, 64
743 )
744 .expect("writing to string should succeed");
745 index += 1;
746 }
747 _ => {}
748 }
749 }
750 }
751
752 let mut metadata_def = String::new();
753 metadata_def.push_str("!llvm.module.flags = !{");
754 for i in 0..index - 1 {
755 write!(metadata_def, "!{i}, ").expect("writing to string should succeed");
756 }
757 writeln!(metadata_def, "!{}}}", index - 1).expect("writing to string should succeed");
758 metadata_def + &flags
759}
760