Skip to content
Merged
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
4 changes: 4 additions & 0 deletions source/compiler/qsc_circuit/src/rir_to_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,10 @@ fn process_variables(
Instruction::LogicalNot(operand, variable) => {
process_logical_not_variables(&mut state.variables, operand, *variable)?;
}
Instruction::Convert(operand, variable) => {
let expr = expr_from_operand(&state.variables, operand)?;
store_expr_in_variable(&mut state.variables, *variable, expr)?;
}
instruction @ (Instruction::Store(..) | Instruction::BitwiseNot(..)) => {
return Err(Error::UnsupportedFeature(format!(
"unsupported instruction in block: {instruction:?}"
Expand Down
28 changes: 28 additions & 0 deletions source/compiler/qsc_codegen/src/qir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@ impl ToQir<String> for rir::Instruction {
rir::Instruction::Call(call_id, args, output, _) => {
call_to_qir(args, *call_id, *output, program)
}
rir::Instruction::Convert(operand, variable) => {
convert_to_qir(operand, *variable, program)
}
rir::Instruction::Fadd(lhs, rhs, variable) => {
fbinop_to_qir("fadd", lhs, rhs, *variable, program)
}
Expand Down Expand Up @@ -356,6 +359,31 @@ impl ToQir<String> for rir::Instruction {
}
}

fn convert_to_qir(
operand: &rir::Operand,
variable: rir::Variable,
program: &rir::Program,
) -> String {
let operand_ty = get_value_ty(operand);
let var_ty = get_variable_ty(variable);
assert_ne!(
operand_ty, var_ty,
"input/output types ({operand_ty}, {var_ty}) should not match in convert"
);

let convert_instr = match (operand_ty, var_ty) {
("i64", "double") => "sitofp i64",
("double", "i64") => "fptosi double",
_ => panic!("unsupported conversion from {operand_ty} to {var_ty} in convert instruction"),
};

format!(
" {} = {convert_instr} {} to {var_ty}",
ToQir::<String>::to_qir(&variable.variable_id, program),
get_value_as_str(operand, program),
)
}

fn logical_not_to_qir(
value: &rir::Operand,
variable: rir::Variable,
Expand Down
28 changes: 28 additions & 0 deletions source/compiler/qsc_codegen/src/qir/instruction_tests/double.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,31 @@ fn fsub_double_variables() {
);
expect![" %var_0 = fsub double %var_1, %var_2"].assert_eq(&inst.to_qir(&Program::default()));
}

#[test]
fn convert_double_literal_to_integer() {
let inst = Instruction::Convert(
Operand::Literal(Literal::Double(PI)),
Variable {
variable_id: VariableId(0),
ty: Ty::Integer,
},
);
expect![" %var_0 = fptosi double 3.141592653589793 to i64"]
.assert_eq(&inst.to_qir(&Program::default()));
}

#[test]
fn convert_double_variable_to_integer() {
let inst = Instruction::Convert(
Operand::Variable(Variable {
variable_id: VariableId(1),
ty: Ty::Double,
}),
Variable {
variable_id: VariableId(0),
ty: Ty::Integer,
},
);
expect![" %var_0 = fptosi double %var_1 to i64"].assert_eq(&inst.to_qir(&Program::default()));
}
28 changes: 28 additions & 0 deletions source/compiler/qsc_codegen/src/qir/instruction_tests/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,31 @@ fn sub_integer_variables() {
);
expect![" %var_0 = sub i64 %var_1, %var_2"].assert_eq(&inst.to_qir(&rir::Program::default()));
}

#[test]
fn convert_integer_literal_to_double() {
let inst = rir::Instruction::Convert(
rir::Operand::Literal(rir::Literal::Integer(2)),
rir::Variable {
variable_id: rir::VariableId(0),
ty: rir::Ty::Double,
},
);
expect![" %var_0 = sitofp i64 2 to double"].assert_eq(&inst.to_qir(&rir::Program::default()));
}

#[test]
fn convert_integer_variable_to_double() {
let inst = rir::Instruction::Convert(
rir::Operand::Variable(rir::Variable {
variable_id: rir::VariableId(1),
ty: rir::Ty::Integer,
}),
rir::Variable {
variable_id: rir::VariableId(0),
ty: rir::Ty::Double,
},
);
expect![" %var_0 = sitofp i64 %var_1 to double"]
.assert_eq(&inst.to_qir(&rir::Program::default()));
}
22 changes: 22 additions & 0 deletions source/compiler/qsc_partial_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,14 @@ impl<'a> PartialEvaluator<'a> {
callee_expr_span,
))
}
"IntAsDouble" => {
let variable_id = self.resource_manager.next_var();
self.convert_value(&args_value, rir::Variable::new_double(variable_id))
}
"Truncate" => {
let variable_id = self.resource_manager.next_var();
self.convert_value(&args_value, rir::Variable::new_integer(variable_id))
}
_ => self.eval_expr_call_to_intrinsic_qis(
store_item_id,
callable_decl,
Expand Down Expand Up @@ -3156,6 +3164,20 @@ impl<'a> PartialEvaluator<'a> {
}
}

fn convert_value(
&mut self,
args_value: &Value,
variable: rir::Variable,
) -> Result<Value, Error> {
let instruction =
Instruction::Convert(self.map_eval_value_to_rir_operand(args_value), variable);
let current_block = self.get_current_rir_block_mut();
current_block.0.push(instruction);
Ok(Value::Var(
map_rir_var_to_eval_var(variable).expect("variable should convert"),
))
}

fn update_bindings(&mut self, lhs_expr_id: ExprId, rhs_value: Value) -> Result<(), Error> {
let lhs_expr = self.get_expr(lhs_expr_id);
match (&lhs_expr.kind, rhs_value) {
Expand Down
82 changes: 82 additions & 0 deletions source/compiler/qsc_partial_eval/src/tests/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,3 +718,85 @@ fn string_concatenation_with_side_effects_captures_side_effects() {
Return"#]],
);
}

#[test]
fn integer_to_double_conversion() {
let program = get_rir_program(indoc! {
r#"
namespace Test {
@EntryPoint()
operation Main() : Double {
let i = {use q = Qubit(); if MResetZ(q) == One { 42 } else { 0 }};
let d = Std.Convert.IntAsDouble(i);
return d;
}
}
"#,
});

assert_blocks(
&program,
&expect![[r#"
Blocks:
Block 0:Block:
Call id(1), args( Pointer, )
Call id(2), args( Qubit(0), Result(0), )
Variable(0, Boolean) = Call id(3), args( Result(0), )
Variable(1, Boolean) = Store Variable(0, Boolean)
Branch Variable(1, Boolean), 2, 3
Block 1:Block:
Variable(3, Integer) = Store Variable(2, Integer)
Variable(4, Integer) = Store Variable(3, Integer)
Variable(5, Double) = Convert Variable(4, Integer)
Variable(6, Double) = Store Variable(5, Double)
Call id(4), args( Variable(6, Double), Tag(0, 3), )
Return
Block 2:Block:
Variable(2, Integer) = Store Integer(42)
Jump(1)
Block 3:Block:
Variable(2, Integer) = Store Integer(0)
Jump(1)"#]],
);
}

#[test]
fn double_to_integer_conversion() {
let program = get_rir_program(indoc! {
r#"
namespace Test {
@EntryPoint()
operation Main() : Int {
let d = {use q = Qubit(); if MResetZ(q) == One { 42.1 } else { 0.0 }};
let i = Std.Math.Truncate(d);
return i;
}
}
"#,
});

assert_blocks(
&program,
&expect![[r#"
Blocks:
Block 0:Block:
Call id(1), args( Pointer, )
Call id(2), args( Qubit(0), Result(0), )
Variable(0, Boolean) = Call id(3), args( Result(0), )
Variable(1, Boolean) = Store Variable(0, Boolean)
Branch Variable(1, Boolean), 2, 3
Block 1:Block:
Variable(3, Double) = Store Variable(2, Double)
Variable(4, Double) = Store Variable(3, Double)
Variable(5, Integer) = Convert Variable(4, Double)
Variable(6, Integer) = Store Variable(5, Integer)
Call id(4), args( Variable(6, Integer), Tag(0, 3), )
Return
Block 2:Block:
Variable(2, Double) = Store Double(42.1)
Jump(1)
Block 3:Block:
Variable(2, Double) = Store Double(0)
Jump(1)"#]],
);
}
4 changes: 3 additions & 1 deletion source/compiler/qsc_rir/src/passes/ssa_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ fn get_variable_uses(program: &Program) -> IndexMap<VariableId, Vec<(BlockId, us
| Instruction::BitwiseOr(Operand::Literal(_), Operand::Variable(var), _)
| Instruction::BitwiseXor(Operand::Variable(var), Operand::Literal(_), _)
| Instruction::BitwiseXor(Operand::Literal(_), Operand::Variable(var), _)
| Instruction::Convert(Operand::Variable(var), _)
| Instruction::Branch(var, _, _, _) => {
add_use(var.variable_id, block_id, idx);
}
Expand Down Expand Up @@ -215,7 +216,8 @@ fn get_variable_uses(program: &Program) -> IndexMap<VariableId, Vec<(BlockId, us
| Instruction::BitwiseNot(Operand::Literal(_), _)
| Instruction::BitwiseAnd(Operand::Literal(_), Operand::Literal(_), _)
| Instruction::BitwiseOr(Operand::Literal(_), Operand::Literal(_), _)
| Instruction::BitwiseXor(Operand::Literal(_), Operand::Literal(_), _) => {
| Instruction::BitwiseXor(Operand::Literal(_), Operand::Literal(_), _)
| Instruction::Convert(Operand::Literal(_), _) => {
panic!("{block_id:?}, instruction {idx} has no variables: {instr}")
}

Expand Down
5 changes: 5 additions & 0 deletions source/compiler/qsc_rir/src/passes/ssa_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ fn map_variable_use_in_block(block: &mut Block, var_map: &mut FxHashMap<Variable
*operand = operand.mapped(var_map);
}

Instruction::Convert(operand, var) => {
*operand = operand.mapped(var_map);
*var = var.map_to_variable(var_map);
}

// Phi nodes are handled separately in the SSA transformation, but need to be passed through
// like the unconditional terminators.
Instruction::Phi(..) | Instruction::Jump(..) | Instruction::Return => {}
Expand Down
2 changes: 1 addition & 1 deletion source/compiler/qsc_rir/src/passes/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn check_instr_types(program: &Program, instr: &Instruction) {
}
}

Instruction::Jump(_) | Instruction::Return => {}
Instruction::Convert(_, _) | Instruction::Jump(_) | Instruction::Return => {}
}
}

Expand Down
5 changes: 5 additions & 0 deletions source/compiler/qsc_rir/src/rir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ pub enum Instruction {
BitwiseOr(Operand, Operand, Variable),
BitwiseXor(Operand, Operand, Variable),
Phi(Vec<(Operand, BlockId)>, Variable),
Convert(Operand, Variable),
Return,
}

Expand Down Expand Up @@ -532,6 +533,10 @@ impl Display for Instruction {
Self::Phi(args, variable) => {
write_phi_instruction(f, args, *variable)?;
}
Self::Convert(operand, variable) => {
let mut indent = set_indentation(indented(f), 0);
write!(indent, "{variable} = Convert {operand}")?;
}
Self::Return => write!(f, "Return")?,
}
Ok(())
Expand Down
1 change: 1 addition & 0 deletions source/compiler/qsc_rir/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub fn get_variable_assignments(program: &Program) -> IndexMap<VariableId, (Bloc
for (idx, instr) in block.0.iter().enumerate() {
match instr {
Instruction::Call(_, _, Some(var), _)
| Instruction::Convert(_, var)
| Instruction::Add(_, _, var)
| Instruction::Sub(_, _, var)
| Instruction::Mul(_, _, var)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Test {
// Demonstrates use of double comparisons.
// Expected output: (10.0, true, false, true, true, false)
@EntryPoint()
operation Main() : (Double, Bool, Bool, Bool, Bool, Bool) {
operation Main() : (Double, Bool, Bool, Bool, Bool, Bool, Int, Double) {
mutable count = 0.0;
use q = Qubit();
for _ in 1..10 {
Expand All @@ -20,6 +20,8 @@ namespace Test {
}
}
Reset(q);
return (count, count > 5.0, count < 5.0, count >= 10.0, count == 10.0, count != 10.0);
let countInteger = Std.Math.Truncate(count);
let countIntegerAsDouble = Std.Convert.IntAsDouble(countInteger);
return (count, count > 5.0, count < 5.0, count >= 10.0, count == 10.0, count != 10.0, countInteger, countIntegerAsDouble);
}
}
Loading
Loading