microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
minestarks-patch-1

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qir.rs

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