Skip to content

Emit spec compliant QIR #2590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
493 changes: 339 additions & 154 deletions source/compiler/qsc/src/codegen/tests.rs

Large diffs are not rendered by default.

114 changes: 82 additions & 32 deletions source/compiler/qsc/src/interpret/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,14 +914,20 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_r\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
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))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__result_record_output(%Result*, i8*)
Expand Down Expand Up @@ -975,16 +981,22 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_r\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
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))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__rz__body(double, %Qubit*)

declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
Expand All @@ -1002,7 +1014,7 @@ mod given_interpreter {
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
!4 = !{i32 1, !"int_computations", !"i64"}
!4 = !{i32 5, !"int_computations", !{!"i64"}}
"#]]
.assert_eq(&res);
}
Expand Down Expand Up @@ -1032,23 +1044,31 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [6 x i8] c"0_t0r\00"
@1 = internal constant [8 x i8] c"1_t1t0b\00"
@2 = internal constant [8 x i8] c"2_t1t1b\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
%var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
%var_2 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
%var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
%var_2 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
%var_3 = icmp eq i1 %var_2, false
call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* null)
call void @__quantum__rt__bool_record_output(i1 %var_3, i8* null)
ret void
call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0))
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))
call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0))
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @1, i64 0, i64 0))
call void @__quantum__rt__bool_record_output(i1 %var_3, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @2, i64 0, i64 0))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1

declare i1 @__quantum__qis__read_result__body(%Result*)
declare i1 @__quantum__rt__read_result(%Result*)

declare void @__quantum__rt__tuple_record_output(i64, i8*)

Expand Down Expand Up @@ -1109,14 +1129,20 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_r\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
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))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__result_record_output(%Result*, i8*)
Expand Down Expand Up @@ -1153,14 +1179,20 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_r\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
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))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__result_record_output(%Result*, i8*)
Expand Down Expand Up @@ -1229,14 +1261,20 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_r\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
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))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__result_record_output(%Result*, i8*)
Expand Down Expand Up @@ -1285,14 +1323,20 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_r\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
ret void
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))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__result_record_output(%Result*, i8*)
Expand Down Expand Up @@ -1340,14 +1384,20 @@ mod given_interpreter {
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
@empty_tag = internal constant [1 x i8] c"\00"
@0 = internal constant [4 x i8] c"0_b\00"

define i64 @ENTRYPOINT__main() #0 {
block_0:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
%var_0 = call i1 @check_result(%Result* inttoptr (i64 0 to %Result*))
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* null)
ret void
call void @__quantum__rt__bool_record_output(i1 %var_0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
ret i64 0
}

declare void @__quantum__rt__initialize(i8*)

declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1

declare i1 @check_result(%Result*)
Expand Down
37 changes: 30 additions & 7 deletions source/compiler/qsc_codegen/src/qir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@ impl ToQir<String> for rir::Literal {
rir::Literal::Pointer => "i8* null".to_string(),
rir::Literal::Qubit(q) => format!("%Qubit* inttoptr (i64 {q} to %Qubit*)"),
rir::Literal::Result(r) => format!("%Result* inttoptr (i64 {r} to %Result*)"),
rir::Literal::Tag(idx, len) => {
let len = len + 1; // +1 for the null terminator
format!(
"i8* getelementptr inbounds ([{len} x i8], [{len} x i8]* @{idx}, i64 0, i64 0)"
)
}
rir::Literal::EmptyTag => {
"i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0)"
.to_string()
}
}
}
}
Expand Down Expand Up @@ -273,7 +283,7 @@ impl ToQir<String> for rir::Instruction {
format!(" br label %{}", ToQir::<String>::to_qir(block_id, program))
}
rir::Instruction::Phi(args, variable) => phi_to_qir(args, *variable, program),
rir::Instruction::Return => " ret void".to_string(),
rir::Instruction::Return => " ret i64 0".to_string(),
rir::Instruction::Sdiv(lhs, rhs, variable) => {
binop_to_qir("sdiv", lhs, rhs, *variable, program)
}
Expand Down Expand Up @@ -571,6 +581,9 @@ fn get_value_as_str(value: &rir::Operand, program: &rir::Program) -> String {
rir::Literal::Pointer => "null".to_string(),
rir::Literal::Qubit(q) => format!("{q}"),
rir::Literal::Result(r) => format!("{r}"),
rir::Literal::Tag(..) | rir::Literal::EmptyTag => panic!(
"tag literals should not be used as string values outside of output recording"
),
},
rir::Operand::Variable(var) => ToQir::<String>::to_qir(&var.variable_id, program),
}
Expand All @@ -584,7 +597,7 @@ fn get_value_ty(lhs: &rir::Operand) -> &str {
rir::Literal::Double(_) => get_f64_ty(),
rir::Literal::Qubit(_) => "%Qubit*",
rir::Literal::Result(_) => "%Result*",
rir::Literal::Pointer => "i8*",
rir::Literal::Pointer | rir::Literal::Tag(..) | rir::Literal::EmptyTag => "i8*",
},
rir::Operand::Variable(var) => get_variable_ty(*var),
}
Expand Down Expand Up @@ -690,9 +703,19 @@ impl ToQir<String> for rir::Program {
} else {
"adaptive_profile"
};
let mut constants = String::default();
for (idx, tag) in self.tags.iter().enumerate() {
// We need to add the tag as a global constant.
writeln!(
constants,
"@{idx} = internal constant [{} x i8] c\"{tag}\\00\"",
tag.len() + 1
)
.expect("writing to string should succeed");
}
let body = format!(
include_str!("./qir/template.ll"),
callables, profile, self.num_qubits, self.num_results
constants, callables, profile, self.num_qubits, self.num_results
);
let flags = get_module_metadata(self);
body + "\n" + &flags
Expand Down Expand Up @@ -728,8 +751,8 @@ fn get_module_metadata(program: &rir::Program) -> String {
let name = "int_computations";
writeln!(
flags,
"!{} = !{{i32 {}, !\"{}\", !\"i{}\"}}",
index, 1, name, 64
"!{} = !{{i32 {}, !\"{}\", !{{!\"i{}\"}}}}",
index, 5, name, 64
)
.expect("writing to string should succeed");
index += 1;
Expand All @@ -738,8 +761,8 @@ fn get_module_metadata(program: &rir::Program) -> String {
let name = "float_computations";
writeln!(
flags,
"!{} = !{{i32 {}, !\"{}\", !\"f{}\"}}",
index, 1, name, 64
"!{} = !{{i32 {}, !\"{}\", !{{!\"f{}\"}}}}",
index, 5, name, 64
)
.expect("writing to string should succeed");
index += 1;
Expand Down
2 changes: 2 additions & 0 deletions source/compiler/qsc_codegen/src/qir/template.ll
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
%Result = type opaque
%Qubit = type opaque

@empty_tag = internal constant [1 x i8] c"\00"
{}
{}

attributes #0 = {{ "entry_point" "output_labeling_schema" "qir_profiles"="{}" "required_num_qubits"="{}" "required_num_results"="{}" }}
Expand Down
Loading
Loading