Skip to content
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

Adaptive profile floating-point computations extension #2078

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
1,023 changes: 659 additions & 364 deletions compiler/qsc/src/codegen/tests.rs

Large diffs are not rendered by default.

19 changes: 3 additions & 16 deletions compiler/qsc/src/interpret/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,19 +968,13 @@ mod given_interpreter {

; module flags

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
!llvm.module.flags = !{!0, !1, !2, !3, !4}

!0 = !{i32 1, !"qir_major_version", i32 1}
!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, !"classical_ints", i1 true}
!5 = !{i32 1, !"qubit_resetting", i1 true}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}
billti marked this conversation as resolved.
Show resolved Hide resolved
!4 = !{i32 1, !"int_computations", !"i64"}
"#]]
.assert_eq(&res);
}
Expand Down Expand Up @@ -1039,19 +1033,12 @@ mod given_interpreter {

; module flags

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
!llvm.module.flags = !{!0, !1, !2, !3}

!0 = !{i32 1, !"qir_major_version", i32 1}
!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, !"qubit_resetting", i1 true}
!5 = !{i32 1, !"classical_ints", i1 false}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}
"#]]
.assert_eq(&res);
}
Expand Down
17 changes: 13 additions & 4 deletions compiler/qsc/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum Profile {
Unrestricted,
Base,
AdaptiveRI,
AdaptiveRIF,
}

impl Profile {
Expand All @@ -19,6 +20,7 @@ impl Profile {
Self::Unrestricted => "Unrestricted",
Self::Base => "Base",
Self::AdaptiveRI => "Adaptive_RI",
Self::AdaptiveRIF => "Adaptive_RIF",
}
}
}
Expand All @@ -29,6 +31,12 @@ impl From<Profile> for TargetCapabilityFlags {
Profile::Unrestricted => Self::all(),
Profile::Base => Self::empty(),
Profile::AdaptiveRI => Self::Adaptive | Self::QubitReset | Self::IntegerComputations,
Profile::AdaptiveRIF => {
Self::Adaptive
| Self::QubitReset
| Self::IntegerComputations
| Self::FloatingPointComputations
}
}
}
}
Expand All @@ -37,10 +45,11 @@ impl FromStr for Profile {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Adaptive_RI" | "adaptive_ri" => Ok(Self::AdaptiveRI),
"Base" | "base" => Ok(Self::Base),
"Unrestricted" | "unrestricted" => Ok(Self::Unrestricted),
match s.to_lowercase().as_str() {
"adaptive_ri" => Ok(Self::AdaptiveRI),
"adaptive_rif" => Ok(Self::AdaptiveRIF),
"base" => Ok(Self::Base),
"unrestricted" => Ok(Self::Unrestricted),
_ => Err(()),
}
}
Expand Down
174 changes: 127 additions & 47 deletions compiler/qsc_codegen/src/qir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use qsc_partial_eval::{partially_evaluate, ProgramEntry};
use qsc_rca::PackageStoreComputeProperties;
use qsc_rir::{
passes::check_and_transform,
rir::{self, ConditionCode},
rir::{self, ConditionCode, FcmpConditionCode},
utils::get_all_block_successors,
};

Expand Down Expand Up @@ -136,6 +136,29 @@ impl ToQir<String> for rir::Operand {
}
}

impl ToQir<String> for rir::FcmpConditionCode {
fn to_qir(&self, _program: &rir::Program) -> String {
match self {
rir::FcmpConditionCode::False => "false".to_string(),
rir::FcmpConditionCode::OrderedAndEqual => "oeq".to_string(),
rir::FcmpConditionCode::OrderedAndGreaterThan => "ogt".to_string(),
rir::FcmpConditionCode::OrderedAndGreaterThanOrEqual => "oge".to_string(),
rir::FcmpConditionCode::OrderedAndLessThan => "olt".to_string(),
rir::FcmpConditionCode::OrderedAndLessThanOrEqual => "ole".to_string(),
rir::FcmpConditionCode::OrderedAndNotEqual => "one".to_string(),
rir::FcmpConditionCode::Ordered => "ord".to_string(),
rir::FcmpConditionCode::UnorderedOrEqual => "ueq".to_string(),
rir::FcmpConditionCode::UnorderedOrGreaterThan => "ugt".to_string(),
rir::FcmpConditionCode::UnorderedOrGreaterThanOrEqual => "uge".to_string(),
rir::FcmpConditionCode::UnorderedOrLessThan => "ult".to_string(),
rir::FcmpConditionCode::UnorderedOrLessThanOrEqual => "ule".to_string(),
rir::FcmpConditionCode::UnorderedOrNotEqual => "une".to_string(),
rir::FcmpConditionCode::Unordered => "uno".to_string(),
rir::FcmpConditionCode::True => "true".to_string(),
idavis marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

impl ToQir<String> for rir::ConditionCode {
fn to_qir(&self, _program: &rir::Program) -> String {
match self {
Expand Down Expand Up @@ -181,6 +204,18 @@ impl ToQir<String> for rir::Instruction {
rir::Instruction::Call(call_id, args, output) => {
call_to_qir(args, *call_id, *output, program)
}
rir::Instruction::Fadd(lhs, rhs, variable) => {
fbinop_to_qir("fadd", lhs, rhs, *variable, program)
}
rir::Instruction::Fdiv(lhs, rhs, variable) => {
fbinop_to_qir("fdiv", lhs, rhs, *variable, program)
}
rir::Instruction::Fmul(lhs, rhs, variable) => {
fbinop_to_qir("fmul", lhs, rhs, *variable, program)
}
rir::Instruction::Fsub(lhs, rhs, variable) => {
fbinop_to_qir("fsub", lhs, rhs, *variable, program)
}
rir::Instruction::LogicalAnd(lhs, rhs, variable) => {
logical_binop_to_qir("and", lhs, rhs, *variable, program)
}
Expand All @@ -193,6 +228,9 @@ impl ToQir<String> for rir::Instruction {
rir::Instruction::Mul(lhs, rhs, variable) => {
binop_to_qir("mul", lhs, rhs, *variable, program)
}
rir::Instruction::Fcmp(op, lhs, rhs, variable) => {
fcmp_to_qir(*op, lhs, rhs, *variable, program)
}
rir::Instruction::Icmp(op, lhs, rhs, variable) => {
icmp_to_qir(*op, lhs, rhs, *variable, program)
}
Expand Down Expand Up @@ -314,6 +352,31 @@ fn call_to_qir(
}
}

fn fcmp_to_qir(
op: FcmpConditionCode,
lhs: &rir::Operand,
rhs: &rir::Operand,
variable: rir::Variable,
program: &rir::Program,
) -> String {
let lhs_ty = get_value_ty(lhs);
let rhs_ty = get_value_ty(rhs);
let var_ty = get_variable_ty(variable);
assert_eq!(
lhs_ty, rhs_ty,
"mismatched input types ({lhs_ty}, {rhs_ty}) for fcmp {op}"
);

assert_eq!(var_ty, "i1", "unsupported output type {var_ty} for fcmp");
format!(
" {} = fcmp {} {lhs_ty} {}, {}",
ToQir::<String>::to_qir(&variable.variable_id, program),
ToQir::<String>::to_qir(&op, program),
get_value_as_str(lhs, program),
get_value_as_str(rhs, program)
)
}

fn icmp_to_qir(
op: ConditionCode,
lhs: &rir::Operand,
Expand Down Expand Up @@ -367,6 +430,34 @@ fn binop_to_qir(
)
}

fn fbinop_to_qir(
op: &str,
lhs: &rir::Operand,
rhs: &rir::Operand,
variable: rir::Variable,
program: &rir::Program,
) -> String {
let lhs_ty = get_value_ty(lhs);
let rhs_ty = get_value_ty(rhs);
let var_ty = get_variable_ty(variable);
assert_eq!(
lhs_ty, rhs_ty,
"mismatched input types ({lhs_ty}, {rhs_ty}) for {op}"
);
assert_eq!(
lhs_ty, var_ty,
"mismatched input/output types ({lhs_ty}, {var_ty}) for {op}"
);
assert_eq!(var_ty, "double", "unsupported type {var_ty} for {op}");

format!(
" {} = {op} {var_ty} {}, {}",
ToQir::<String>::to_qir(&variable.variable_id, program),
get_value_as_str(lhs, program),
get_value_as_str(rhs, program)
)
}

fn simple_bitwise_to_qir(
op: &str,
lhs: &rir::Operand,
Expand Down Expand Up @@ -455,7 +546,7 @@ fn get_value_ty(lhs: &rir::Operand) -> &str {
rir::Operand::Literal(lit) => match lit {
rir::Literal::Integer(_) => "i64",
rir::Literal::Bool(_) => "i1",
rir::Literal::Double(_) => "f64",
rir::Literal::Double(_) => get_f64_ty(),
billti marked this conversation as resolved.
Show resolved Hide resolved
rir::Literal::Qubit(_) => "%Qubit*",
rir::Literal::Result(_) => "%Result*",
rir::Literal::Pointer => "i8*",
Expand All @@ -468,13 +559,28 @@ fn get_variable_ty(variable: rir::Variable) -> &'static str {
match variable.ty {
rir::Ty::Integer => "i64",
rir::Ty::Boolean => "i1",
rir::Ty::Double => "f64",
rir::Ty::Double => get_f64_ty(),
rir::Ty::Qubit => "%Qubit*",
rir::Ty::Result => "%Result*",
rir::Ty::Pointer => "i8*",
}
}

/// phi only supports "Floating-Point Types" which are defined as:
/// - `half` (`f16`)
/// - `bfloat`
/// - `float` (`f32`)
/// - `double` (`f64`)
/// - `fp128`
///
/// We only support `f64`, so we break the pattern used for integers
/// and have to use `double` here.
///
/// This conflicts with the QIR spec which says f64. Need to follow up on this.
fn get_f64_ty() -> &'static str {
"double"
}

impl ToQir<String> for rir::BlockId {
fn to_qir(&self, _program: &rir::Program) -> String {
format!("block_{}", self.0)
Expand Down Expand Up @@ -580,51 +686,25 @@ fn get_module_metadata(program: &rir::Program) -> String {
// loop through the capabilities and add them to the metadata
// for values that we can generate.
for cap in program.config.capabilities.iter() {
let name = match cap {
TargetCapabilityFlags::QubitReset => "qubit_resetting",
TargetCapabilityFlags::IntegerComputations => "classical_ints",
TargetCapabilityFlags::FloatingPointComputations => "classical_floats",
TargetCapabilityFlags::BackwardsBranching => "backwards_branching",
_ => continue,
};
flags.push_str(&format!(
"!{} = !{{i32 {}, !\"{}\", i1 {}}}\n",
index, 1, name, true
));
index += 1;
}

// loop through the capabilities that are missing and add them to the metadata
// as not supported.
let missing = TargetCapabilityFlags::all().difference(program.config.capabilities);
for cap in missing.iter() {
let name = match cap {
TargetCapabilityFlags::QubitReset => "qubit_resetting",
TargetCapabilityFlags::IntegerComputations => "classical_ints",
TargetCapabilityFlags::FloatingPointComputations => "classical_floats",
TargetCapabilityFlags::BackwardsBranching => "backwards_branching",
match cap {
TargetCapabilityFlags::IntegerComputations => {
let name = "int_computations";
flags.push_str(&format!(
"!{} = !{{i32 {}, !\"{}\", !\"i{}\"}}\n",
index, 1, name, 64
));
index += 1;
}
TargetCapabilityFlags::FloatingPointComputations => {
let name = "float_computations";
flags.push_str(&format!(
"!{} = !{{i32 {}, !\"{}\", !\"f{}\"}}\n",
index, 1, name, 64
));
index += 1;
}
_ => continue,
};
flags.push_str(&format!(
"!{} = !{{i32 {}, !\"{}\", i1 {}}}\n",
index, 1, name, false
));
index += 1;
}

// Add the remaining extension capabilities as not supported.
// We can't generate these values yet so we just add them as false.
let unmapped_capabilities = [
"classical_fixed_points",
"user_functions",
"multiple_target_branching",
];
for capability in unmapped_capabilities {
flags.push_str(&format!(
"!{} = !{{i32 {}, !\"{}\", i1 {}}}\n",
index, 1, capability, false
));
index += 1;
}
}
}

Expand Down
Loading
Loading