microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/pipeline-issue-debugging

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc_codegen/src/qir.rs

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