Skip to content

Commit 95a6a6a

Browse files
authored
Emit spec compliant QIR (#2590)
This updates the QIR code generation logic to comply with the Base and Adaptive Profile specifications. In particular, it makes the following updates: - The entry point function returns `i64`. - Module level flags for computation extensions are updated to be consistent with spec fixes for LLVM flags. - QIR now includes global strings in the data section that are used as tags for the output recording calls, with tag values chosen to be consistent with the Azure Quantum service output translation. - The `__quantum__qis__read_result__body` function is renamed to `__quantum__rt__read_result`. - Every program begins with a call to the `__quantum__rt__initialize` function.
1 parent f2ca520 commit 95a6a6a

File tree

97 files changed

+6496
-5432
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+6496
-5432
lines changed

source/compiler/qsc/src/codegen/tests.rs

Lines changed: 339 additions & 154 deletions
Large diffs are not rendered by default.

source/compiler/qsc/src/interpret/tests.rs

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -993,14 +993,20 @@ mod given_interpreter {
993993
%Result = type opaque
994994
%Qubit = type opaque
995995
996-
define void @ENTRYPOINT__main() #0 {
996+
@empty_tag = internal constant [1 x i8] c"\00"
997+
@0 = internal constant [4 x i8] c"0_r\00"
998+
999+
define i64 @ENTRYPOINT__main() #0 {
9971000
block_0:
1001+
call void @__quantum__rt__initialize(i8* null)
9981002
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
9991003
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1000-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1001-
ret void
1004+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1005+
ret i64 0
10021006
}
10031007
1008+
declare void @__quantum__rt__initialize(i8*)
1009+
10041010
declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
10051011
10061012
declare void @__quantum__rt__result_record_output(%Result*, i8*)
@@ -1054,16 +1060,22 @@ mod given_interpreter {
10541060
%Result = type opaque
10551061
%Qubit = type opaque
10561062
1057-
define void @ENTRYPOINT__main() #0 {
1063+
@empty_tag = internal constant [1 x i8] c"\00"
1064+
@0 = internal constant [4 x i8] c"0_r\00"
1065+
1066+
define i64 @ENTRYPOINT__main() #0 {
10581067
block_0:
1068+
call void @__quantum__rt__initialize(i8* null)
10591069
call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
10601070
call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
10611071
call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
10621072
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1063-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1064-
ret void
1073+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1074+
ret i64 0
10651075
}
10661076
1077+
declare void @__quantum__rt__initialize(i8*)
1078+
10671079
declare void @__quantum__qis__rz__body(double, %Qubit*)
10681080
10691081
declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
@@ -1081,7 +1093,7 @@ mod given_interpreter {
10811093
!1 = !{i32 7, !"qir_minor_version", i32 0}
10821094
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
10831095
!3 = !{i32 1, !"dynamic_result_management", i1 false}
1084-
!4 = !{i32 1, !"int_computations", !"i64"}
1096+
!4 = !{i32 5, !"int_computations", !{!"i64"}}
10851097
"#]]
10861098
.assert_eq(&res);
10871099
}
@@ -1111,23 +1123,31 @@ mod given_interpreter {
11111123
%Result = type opaque
11121124
%Qubit = type opaque
11131125
1114-
define void @ENTRYPOINT__main() #0 {
1126+
@empty_tag = internal constant [1 x i8] c"\00"
1127+
@0 = internal constant [6 x i8] c"0_t0r\00"
1128+
@1 = internal constant [8 x i8] c"1_t1t0b\00"
1129+
@2 = internal constant [8 x i8] c"2_t1t1b\00"
1130+
1131+
define i64 @ENTRYPOINT__main() #0 {
11151132
block_0:
1133+
call void @__quantum__rt__initialize(i8* null)
11161134
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1117-
%var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
1118-
%var_2 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
1135+
%var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
1136+
%var_2 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
11191137
%var_3 = icmp eq i1 %var_2, false
1120-
call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1121-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1122-
call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1123-
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* null)
1124-
call void @__quantum__rt__bool_record_output(i1 %var_3, i8* null)
1125-
ret void
1138+
call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0))
1139+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i64 0, i64 0))
1140+
call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0))
1141+
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @1, i64 0, i64 0))
1142+
call void @__quantum__rt__bool_record_output(i1 %var_3, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @2, i64 0, i64 0))
1143+
ret i64 0
11261144
}
11271145
1146+
declare void @__quantum__rt__initialize(i8*)
1147+
11281148
declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
11291149
1130-
declare i1 @__quantum__qis__read_result__body(%Result*)
1150+
declare i1 @__quantum__rt__read_result(%Result*)
11311151
11321152
declare void @__quantum__rt__tuple_record_output(i64, i8*)
11331153
@@ -1188,14 +1208,20 @@ mod given_interpreter {
11881208
%Result = type opaque
11891209
%Qubit = type opaque
11901210
1191-
define void @ENTRYPOINT__main() #0 {
1211+
@empty_tag = internal constant [1 x i8] c"\00"
1212+
@0 = internal constant [4 x i8] c"0_r\00"
1213+
1214+
define i64 @ENTRYPOINT__main() #0 {
11921215
block_0:
1216+
call void @__quantum__rt__initialize(i8* null)
11931217
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
11941218
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1195-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1196-
ret void
1219+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1220+
ret i64 0
11971221
}
11981222
1223+
declare void @__quantum__rt__initialize(i8*)
1224+
11991225
declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
12001226
12011227
declare void @__quantum__rt__result_record_output(%Result*, i8*)
@@ -1232,14 +1258,20 @@ mod given_interpreter {
12321258
%Result = type opaque
12331259
%Qubit = type opaque
12341260
1235-
define void @ENTRYPOINT__main() #0 {
1261+
@empty_tag = internal constant [1 x i8] c"\00"
1262+
@0 = internal constant [4 x i8] c"0_r\00"
1263+
1264+
define i64 @ENTRYPOINT__main() #0 {
12361265
block_0:
1266+
call void @__quantum__rt__initialize(i8* null)
12371267
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
12381268
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1239-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1240-
ret void
1269+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1270+
ret i64 0
12411271
}
12421272
1273+
declare void @__quantum__rt__initialize(i8*)
1274+
12431275
declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
12441276
12451277
declare void @__quantum__rt__result_record_output(%Result*, i8*)
@@ -1308,14 +1340,20 @@ mod given_interpreter {
13081340
%Result = type opaque
13091341
%Qubit = type opaque
13101342
1311-
define void @ENTRYPOINT__main() #0 {
1343+
@empty_tag = internal constant [1 x i8] c"\00"
1344+
@0 = internal constant [4 x i8] c"0_r\00"
1345+
1346+
define i64 @ENTRYPOINT__main() #0 {
13121347
block_0:
1348+
call void @__quantum__rt__initialize(i8* null)
13131349
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
13141350
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1315-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1316-
ret void
1351+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1352+
ret i64 0
13171353
}
13181354
1355+
declare void @__quantum__rt__initialize(i8*)
1356+
13191357
declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
13201358
13211359
declare void @__quantum__rt__result_record_output(%Result*, i8*)
@@ -1364,14 +1402,20 @@ mod given_interpreter {
13641402
%Result = type opaque
13651403
%Qubit = type opaque
13661404
1367-
define void @ENTRYPOINT__main() #0 {
1405+
@empty_tag = internal constant [1 x i8] c"\00"
1406+
@0 = internal constant [4 x i8] c"0_r\00"
1407+
1408+
define i64 @ENTRYPOINT__main() #0 {
13681409
block_0:
1410+
call void @__quantum__rt__initialize(i8* null)
13691411
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
13701412
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1371-
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1372-
ret void
1413+
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1414+
ret i64 0
13731415
}
13741416
1417+
declare void @__quantum__rt__initialize(i8*)
1418+
13751419
declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
13761420
13771421
declare void @__quantum__rt__result_record_output(%Result*, i8*)
@@ -1419,14 +1463,20 @@ mod given_interpreter {
14191463
%Result = type opaque
14201464
%Qubit = type opaque
14211465
1422-
define void @ENTRYPOINT__main() #0 {
1466+
@empty_tag = internal constant [1 x i8] c"\00"
1467+
@0 = internal constant [4 x i8] c"0_b\00"
1468+
1469+
define i64 @ENTRYPOINT__main() #0 {
14231470
block_0:
1471+
call void @__quantum__rt__initialize(i8* null)
14241472
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
14251473
%var_0 = call i1 @check_result(%Result* inttoptr (i64 0 to %Result*))
1426-
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* null)
1427-
ret void
1474+
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1475+
ret i64 0
14281476
}
14291477
1478+
declare void @__quantum__rt__initialize(i8*)
1479+
14301480
declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
14311481
14321482
declare i1 @check_result(%Result*)

source/compiler/qsc_codegen/src/qir.rs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ impl ToQir<String> for rir::Literal {
120120
rir::Literal::Pointer => "i8* null".to_string(),
121121
rir::Literal::Qubit(q) => format!("%Qubit* inttoptr (i64 {q} to %Qubit*)"),
122122
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+
}
123133
}
124134
}
125135
}
@@ -273,7 +283,7 @@ impl ToQir<String> for rir::Instruction {
273283
format!(" br label %{}", ToQir::<String>::to_qir(block_id, program))
274284
}
275285
rir::Instruction::Phi(args, variable) => phi_to_qir(args, *variable, program),
276-
rir::Instruction::Return => " ret void".to_string(),
286+
rir::Instruction::Return => " ret i64 0".to_string(),
277287
rir::Instruction::Sdiv(lhs, rhs, variable) => {
278288
binop_to_qir("sdiv", lhs, rhs, *variable, program)
279289
}
@@ -571,6 +581,9 @@ fn get_value_as_str(value: &rir::Operand, program: &rir::Program) -> String {
571581
rir::Literal::Pointer => "null".to_string(),
572582
rir::Literal::Qubit(q) => format!("{q}"),
573583
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+
),
574587
},
575588
rir::Operand::Variable(var) => ToQir::<String>::to_qir(&var.variable_id, program),
576589
}
@@ -584,7 +597,7 @@ fn get_value_ty(lhs: &rir::Operand) -> &str {
584597
rir::Literal::Double(_) => get_f64_ty(),
585598
rir::Literal::Qubit(_) => "%Qubit*",
586599
rir::Literal::Result(_) => "%Result*",
587-
rir::Literal::Pointer => "i8*",
600+
rir::Literal::Pointer | rir::Literal::Tag(..) | rir::Literal::EmptyTag => "i8*",
588601
},
589602
rir::Operand::Variable(var) => get_variable_ty(*var),
590603
}
@@ -690,9 +703,19 @@ impl ToQir<String> for rir::Program {
690703
} else {
691704
"adaptive_profile"
692705
};
706+
let mut constants = String::default();
707+
for (idx, tag) in self.tags.iter().enumerate() {
708+
// We need to add the tag as a global constant.
709+
writeln!(
710+
constants,
711+
"@{idx} = internal constant [{} x i8] c\"{tag}\\00\"",
712+
tag.len() + 1
713+
)
714+
.expect("writing to string should succeed");
715+
}
693716
let body = format!(
694717
include_str!("./qir/template.ll"),
695-
callables, profile, self.num_qubits, self.num_results
718+
constants, callables, profile, self.num_qubits, self.num_results
696719
);
697720
let flags = get_module_metadata(self);
698721
body + "\n" + &flags
@@ -725,21 +748,19 @@ fn get_module_metadata(program: &rir::Program) -> String {
725748
for cap in program.config.capabilities.iter() {
726749
match cap {
727750
TargetCapabilityFlags::IntegerComputations => {
728-
let name = "int_computations";
751+
// Use `5` as the flag to signify "Append" mode. See https://llvm.org/docs/LangRef.html#module-flags-metadata
729752
writeln!(
730753
flags,
731-
"!{} = !{{i32 {}, !\"{}\", !\"i{}\"}}",
732-
index, 1, name, 64
754+
"!{index} = !{{i32 5, !\"int_computations\", !{{!\"i64\"}}}}",
733755
)
734756
.expect("writing to string should succeed");
735757
index += 1;
736758
}
737759
TargetCapabilityFlags::FloatingPointComputations => {
738-
let name = "float_computations";
760+
// Use `5` as the flag to signify "Append" mode. See https://llvm.org/docs/LangRef.html#module-flags-metadata
739761
writeln!(
740762
flags,
741-
"!{} = !{{i32 {}, !\"{}\", !\"f{}\"}}",
742-
index, 1, name, 64
763+
"!{index} = !{{i32 5, !\"float_computations\", !{{!\"double\"}}}}",
743764
)
744765
.expect("writing to string should succeed");
745766
index += 1;

source/compiler/qsc_codegen/src/qir/template.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
%Result = type opaque
22
%Qubit = type opaque
33

4+
@empty_tag = internal constant [1 x i8] c"\00"
5+
{}
46
{}
57

68
attributes #0 = {{ "entry_point" "output_labeling_schema" "qir_profiles"="{}" "required_num_qubits"="{}" "required_num_results"="{}" }}

0 commit comments

Comments
 (0)