Skip to content
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
9 changes: 6 additions & 3 deletions source/compiler/qsc/benches/rca.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl CompilationContext {
}

fn analyze_all(&mut self) {
let analyzer = Analyzer::init(&self.fir_store);
let analyzer = Analyzer::init(&self.fir_store, TargetCapabilityFlags::all());
let compute_properties = analyzer.analyze_all();
self.compute_properties = Some(compute_properties);
}
Expand All @@ -108,8 +108,11 @@ impl CompilationContext {
package_compute_properties.clear();

// Analyze the open package without re-analyzing the other packages.
let analyzer =
Analyzer::init_with_compute_properties(&self.fir_store, compute_properties.clone());
let analyzer = Analyzer::init_with_compute_properties(
&self.fir_store,
TargetCapabilityFlags::all(),
compute_properties.clone(),
);
self.compute_properties = Some(analyzer.analyze_package(open_package_id));
}

Expand Down
6 changes: 3 additions & 3 deletions source/compiler/qsc_codegen/src/qir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn fir_to_qir_from_callable(
args: Value,
) -> Result<String, qsc_partial_eval::Error> {
let compute_properties = compute_properties.unwrap_or_else(|| {
let analyzer = qsc_rca::Analyzer::init(fir_store);
let analyzer = qsc_rca::Analyzer::init(fir_store, capabilities);
analyzer.analyze_all()
});

Expand Down Expand Up @@ -97,7 +97,7 @@ pub fn fir_to_rir_from_callable(
partial_eval_config: PartialEvalConfig,
) -> Result<(Program, Program), qsc_partial_eval::Error> {
let compute_properties = compute_properties.unwrap_or_else(|| {
let analyzer = qsc_rca::Analyzer::init(fir_store);
let analyzer = qsc_rca::Analyzer::init(fir_store, capabilities);
analyzer.analyze_all()
});

Expand All @@ -122,7 +122,7 @@ fn get_rir_from_compilation(
partial_eval_config: PartialEvalConfig,
) -> Result<rir::Program, qsc_partial_eval::Error> {
let compute_properties = compute_properties.unwrap_or_else(|| {
let analyzer = qsc_rca::Analyzer::init(fir_store);
let analyzer = qsc_rca::Analyzer::init(fir_store, capabilities);
analyzer.analyze_all()
});

Expand Down
108 changes: 105 additions & 3 deletions source/compiler/qsc_partial_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2315,11 +2315,18 @@ impl<'a> PartialEvaluator<'a> {
.get_current_scope()
.get_hybrid_local_value(*local_var_id);

// Check whether the bound value is a mutable variable, and if so, return its value directly rather than
// the variable if it is static at this moment.
// Check whether the bound value is a mutable variable and we are not currently evaluating a branch.
// If so, return its value directly rather than the variable if it is static at this moment.
if let Value::Var(var) = bound_value {
let current_scope = self.eval_context.get_current_scope();
if let Some(literal) = current_scope.get_static_value(var.id.into()) {
if let Some(literal) = current_scope.get_static_value(var.id.into())
&& (!current_scope.is_currently_evaluating_branch()
|| !self
.program
.config
.capabilities
.contains(TargetCapabilityFlags::BackwardsBranching))
{
map_rir_literal_to_eval_value(*literal)
} else {
bound_value.clone()
Expand All @@ -2337,6 +2344,18 @@ impl<'a> PartialEvaluator<'a> {
condition_expr_id: ExprId,
body_block_id: BlockId,
) -> Result<EvalControlFlow, Error> {
if self
.program
.config
.capabilities
.contains(TargetCapabilityFlags::BackwardsBranching)
&& !self.is_static_expr(condition_expr_id)
{
// If backwards branching is supported and the loop condition is not static,
// we can generate a while loop structure in RIR without unrolling the loop.
return self.eval_expr_emit_while(loop_expr_id, condition_expr_id, body_block_id);
}

// Verify assumptions: the condition expression must either static (such that it can be fully evaluated) or
// dynamic but constant at runtime (such that it can be partially evaluated to a known value).
assert!(
Expand Down Expand Up @@ -2393,6 +2412,89 @@ impl<'a> PartialEvaluator<'a> {
Ok(EvalControlFlow::Continue(Value::unit()))
}

fn eval_expr_emit_while(
&mut self,
loop_expr_id: ExprId,
condition_expr_id: ExprId,
body_block_id: BlockId,
) -> Result<EvalControlFlow, Error> {
// Pop the current block node and create the necessary block nodes for the loop structure.
let current_block_node = self.eval_context.pop_block_node();
let conditional_block_node_id = self.create_program_block();
let conditional_block_node = BlockNode {
id: conditional_block_node_id,
successor: current_block_node.successor,
};
let continuation_block_node_id = self.create_program_block();
let continuation_block_node = BlockNode {
id: continuation_block_node_id,
successor: current_block_node.successor,
};
self.eval_context.push_block_node(continuation_block_node);

// Insert the jump instruction to the conditional block from the current block.
let jump_to_condition_ins = Instruction::Jump(conditional_block_node_id);
self.get_program_block_mut(current_block_node.id)
.0
.push(jump_to_condition_ins);

// In the conditional block, evaluate the condition expression and generate the branch instruction.
self.eval_context.push_block_node(conditional_block_node);
let condition_control_flow = self.try_eval_expr(condition_expr_id)?;
if condition_control_flow.is_return() {
return Err(Error::Unexpected(
"embedded return in loop condition".to_string(),
self.get_expr_package_span(condition_expr_id),
));
}
let condition_value = condition_control_flow.into_value();

if let Value::Bool(false) = condition_value {
// If the condition is statically false, jump directly to the continuation block.
let jump_to_continuation_ins = Instruction::Jump(continuation_block_node_id);
self.get_current_rir_block_mut()
.0
.push(jump_to_continuation_ins);
let _ = self.eval_context.pop_block_node();
return Ok(EvalControlFlow::Continue(Value::unit()));
}

// Otherwise, branch to either the body block or the continuation block.
let body_block_node_id = self.create_program_block();
let body_block_node = BlockNode {
id: body_block_node_id,
successor: Some(conditional_block_node_id),
};
let condition_value_var = condition_value.unwrap_var();
let condition_rir_var = map_eval_var_to_rir_var(condition_value_var);
let metadata = self.metadata_from_expr(loop_expr_id);
let branch_ins = Instruction::Branch(
condition_rir_var,
body_block_node_id,
continuation_block_node_id,
metadata,
);
self.get_current_rir_block_mut().0.push(branch_ins);
let _ = self.eval_context.pop_block_node();

// In the body block, evaluate the loop body and jump back to the conditional block.
self.eval_context.push_block_node(body_block_node);
let body_control_flow = self.try_eval_block(body_block_id)?;
if body_control_flow.is_return() {
return Err(Error::Unexpected(
"embedded return in loop body".to_string(),
self.get_expr_package_span(condition_expr_id),
));
}
let jump_to_condition_ins = Instruction::Jump(conditional_block_node_id);
self.get_current_rir_block_mut()
.0
.push(jump_to_condition_ins);
let _ = self.eval_context.pop_block_node();

Ok(EvalControlFlow::Continue(Value::unit()))
}

fn eval_result_as_bool_operand(&mut self, result: val::Result) -> Operand {
match result {
val::Result::Id(id) => {
Expand Down
30 changes: 3 additions & 27 deletions source/compiler/qsc_partial_eval/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,7 @@ pub fn assert_error(error: &Error, expected_error: &Expect) {

#[must_use]
pub fn get_partial_evaluation_error(source: &str) -> Error {
let maybe_program = compile_and_partially_evaluate(
source,
TargetCapabilityFlags::all(),
PartialEvalConfig {
generate_debug_metadata: false,
},
);
match maybe_program {
Ok(_) => panic!("partial evaluation succeeded"),
Err(error) => error,
}
get_partial_evaluation_error_with_capabilities(source, Profile::AdaptiveRIF.into())
}

#[must_use]
Expand All @@ -100,21 +90,7 @@ pub fn get_partial_evaluation_error_with_capabilities(

#[must_use]
pub fn get_rir_program(source: &str) -> Program {
let maybe_program = compile_and_partially_evaluate(
source,
Profile::AdaptiveRIF.into(),
PartialEvalConfig {
generate_debug_metadata: false,
},
);
match maybe_program {
Ok(program) => {
// Verify the program can go through transformations.
check_and_transform(&mut program.clone());
program
}
Err(error) => panic!("partial evaluation failed: {error:?}"),
}
get_rir_program_with_capabilities(source, Profile::AdaptiveRIF.into())
}

#[must_use]
Expand Down Expand Up @@ -242,7 +218,7 @@ impl CompilationContext {
.expect("should be able to create a new compiler");
let package_id = map_hir_package_to_fir(compiler.source_package_id());
let fir_store = lower_hir_package_store(compiler.package_store());
let analyzer = Analyzer::init(&fir_store);
let analyzer = Analyzer::init(&fir_store, capabilities);
let compute_properties = analyzer.analyze_all();
let package = fir_store.get(package_id);
let entry = ProgramEntry {
Expand Down
Loading
Loading