microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
brlackey/neutral-atom-models

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc_codegen/src/qir/v1.rs

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