Skip to content

Commit 06f1fa5

Browse files
authored
Codegen for basic branching on measurement (#1311)
This change upgrades RIR -> QIR to support basic branching on measurement results via `Jump` and `Branch` instructions. This does not include any support for mutable variables.
1 parent 37122e1 commit 06f1fa5

File tree

8 files changed

+379
-29
lines changed

8 files changed

+379
-29
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/qsc_codegen/src/qir.rs

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod rir_builder;
66
#[cfg(test)]
77
mod tests;
88

9-
use qsc_rir::rir;
9+
use qsc_rir::{rir, utils::get_all_block_successors};
1010

1111
/// A trait for converting a type into QIR of type `T`.
1212
/// This can be used to generate QIR strings or other representations.
@@ -86,22 +86,41 @@ impl ToQir<String> for rir::Instruction {
8686
fn to_qir(&self, program: &rir::Program) -> String {
8787
match self {
8888
rir::Instruction::Store(_, _) => unimplemented!("store should be removed by pass"),
89-
rir::Instruction::Call(call_id, args) => {
89+
rir::Instruction::Call(call_id, args, output) => {
9090
let args = args
9191
.iter()
9292
.map(|arg| ToQir::<String>::to_qir(arg, program))
9393
.collect::<Vec<_>>()
9494
.join(", ");
9595
let callable = program.get_callable(*call_id);
96+
if let Some(output) = output {
97+
format!(
98+
" {} = call {} @{}({})",
99+
ToQir::<String>::to_qir(&output.variable_id, program),
100+
ToQir::<String>::to_qir(&callable.output_type, program),
101+
callable.name,
102+
args
103+
)
104+
} else {
105+
format!(
106+
" call {} @{}({})",
107+
ToQir::<String>::to_qir(&callable.output_type, program),
108+
callable.name,
109+
args
110+
)
111+
}
112+
}
113+
rir::Instruction::Jump(block_id) => {
114+
format!(" br label %{}", ToQir::<String>::to_qir(block_id, program))
115+
}
116+
rir::Instruction::Branch(cond, true_id, false_id) => {
96117
format!(
97-
" call {} @{}({})",
98-
ToQir::<String>::to_qir(&callable.output_type, program),
99-
callable.name,
100-
args
118+
" br {}, label %{}, label %{}",
119+
ToQir::<String>::to_qir(cond, program),
120+
ToQir::<String>::to_qir(true_id, program),
121+
ToQir::<String>::to_qir(false_id, program)
101122
)
102123
}
103-
rir::Instruction::Jump(_) => todo!(),
104-
rir::Instruction::Branch(_, _, _) => todo!(),
105124
rir::Instruction::Add(_, _, _) => todo!(),
106125
rir::Instruction::Sub(_, _, _) => todo!(),
107126
rir::Instruction::Mul(_, _, _) => todo!(),
@@ -118,6 +137,22 @@ impl ToQir<String> for rir::Instruction {
118137
}
119138
}
120139

140+
impl ToQir<String> for rir::BlockId {
141+
fn to_qir(&self, _program: &rir::Program) -> String {
142+
format!("block_{}", self.0)
143+
}
144+
}
145+
146+
impl ToQir<String> for rir::Block {
147+
fn to_qir(&self, program: &rir::Program) -> String {
148+
self.0
149+
.iter()
150+
.map(|instr| ToQir::<String>::to_qir(instr, program))
151+
.collect::<Vec<_>>()
152+
.join("\n")
153+
}
154+
}
155+
121156
impl ToQir<String> for rir::Callable {
122157
fn to_qir(&self, program: &rir::Program) -> String {
123158
let input_type = self
@@ -127,30 +162,33 @@ impl ToQir<String> for rir::Callable {
127162
.collect::<Vec<_>>()
128163
.join(", ");
129164
let output_type = ToQir::<String>::to_qir(&self.output_type, program);
130-
let signature = format!("{} @{}({})", output_type, self.name, input_type);
131165
let Some(entry_id) = self.body else {
132166
return format!(
133-
"declare {signature}{}",
134-
if self.name == "__quantum__qis__mz__body" {
135-
// The mz callable is a special case that needs the irreversable attribute.
167+
"declare {output_type} @{}({input_type}){}",
168+
self.name,
169+
if self.is_measurement {
170+
// Measurement callables are a special case that needs the irreversable attribute.
136171
" #1"
137172
} else {
138173
""
139174
}
140175
);
141176
};
142-
// For now, assume a single block.
143-
let block = program.get_block(entry_id);
144-
let body = block
145-
.0
146-
.iter()
147-
.map(|instr| ToQir::<String>::to_qir(instr, program))
148-
.collect::<Vec<_>>()
149-
.join("\n");
150-
format!(
151-
"define {signature} #0 {{\nblock_{}:\n{body}\n}}",
152-
entry_id.0
153-
)
177+
let mut body = String::new();
178+
let all_blocks = get_all_block_successors(entry_id, program);
179+
for block_id in all_blocks {
180+
let block = program.get_block(block_id);
181+
body.push_str(&format!(
182+
"{}:\n{}\n",
183+
ToQir::<String>::to_qir(&block_id, program),
184+
ToQir::<String>::to_qir(block, program)
185+
));
186+
}
187+
assert!(
188+
input_type.is_empty(),
189+
"entry point should not have an input"
190+
);
191+
format!("define {output_type} @ENTRYPOINT__main() #0 {{\n{body}}}",)
154192
}
155193
}
156194

0 commit comments

Comments
 (0)