Skip to content

RIFLA: Support emission of loops over constant arrays#3101

Open
swernli wants to merge 3 commits intomainfrom
swernli/rifla-arrays
Open

RIFLA: Support emission of loops over constant arrays#3101
swernli wants to merge 3 commits intomainfrom
swernli/rifla-arrays

Conversation

@swernli
Copy link
Copy Markdown
Collaborator

@swernli swernli commented Apr 7, 2026

This updates the Adaptive_RIFLA profile to support emission of loops over arrays with constant, compile-time known content. Where the prior looping support only handled loops with iteration-invariant internals, this updates RCA, Partial Eval, RIR, and QIR codegen to allow for arrays with constant content stored in the global section to be the source of iteration values within a loop.

For now, only loops with variable types we already support work with this pattern, specifically loops over constant arrays of Bool, Int, and Double. Arrays of arrays are not supported, due to the lack of QIR compatible mechanism for dynamically querying the length of an array and Q# support for jagged arrays of arrays (ie: [[1.0], [2.0, 3.0]]). Follow up PRs will add support for iteration over constant arrays of new types like Qubit and Pauli. Arrays with dynamic contents will be handled later down the line.

This updates the `Adaptive_RIFLA` profile to support emission of loops over arrays with constant, compile-time known content. Where the prior looping support only handled loops with iteration-invariant internals, this updates RCA, Partial Eval, RIR, and QIR codegen to allow for arrays with constant content stored in the global section to be the source of iteration values within a loop.

For now, only loops with variable types we already support work with this pattern, specifically loops over constant arrays of `Bool`, `Int`, and `Double`. Arrays of arrays are not supported, due to the lack of QIR compatible mechanism for dynamically querying the length of an array and Q# support for jagged arrays of arrays (ie: `[[1.0], [2.0, 3.0]]`). Follow up PRs will add support for iteration over constant arrays of new types like `Qubit` and `Pauli`. Arrays with dynamic contents will be handled later down the line.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Change in memory usage detected by benchmark.

Memory Report for 76291c3

Test This Branch On Main Difference
compile core + standard lib 24572134 bytes 24572311 bytes -177 bytes

while let Some(block_id) = blocks_to_visit.pop() {
visited_blocks.insert(block_id);
let mut used_vars_in_block = FxHashSet::default();
let mut gep_vars_in_block = FxHashSet::default();
Copy link
Copy Markdown
Contributor

@orpuente-MS orpuente-MS Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "gep" mean?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing "global element pointer"

Comment on lines +3974 to +3989
let array_literal =
convert_to_array_literal(array, array_package_span, index_package_span)?;
let array_elem_ty = array_literal.ty;

let const_array_id = if let Some(idx) = self
.program
.array_literals
.iter()
.position(|a| a == &array_literal)
{
idx
} else {
let idx = self.program.array_literals.len();
self.program.array_literals.push(array_literal);
idx
};
Copy link
Copy Markdown
Contributor

@orpuente-MS orpuente-MS Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting the same array every time we see it to an array-literal, and then iterating over all seen arrays and doing element-wise comparisons feels expensive. I think we could use the data-pointer of the Rc as a key in a hash table to build a cache to reduce the cost.

Will also need to add this field to the PartialEvaluator struct:

array_literal_cache: FxHashMap<*const Vec<Value>, (usize, rir::Prim)>,
Suggested change
let array_literal =
convert_to_array_literal(array, array_package_span, index_package_span)?;
let array_elem_ty = array_literal.ty;
let const_array_id = if let Some(idx) = self
.program
.array_literals
.iter()
.position(|a| a == &array_literal)
{
idx
} else {
let idx = self.program.array_literals.len();
self.program.array_literals.push(array_literal);
idx
};
let array_ptr = Rc::as_ptr(array);
// Fast path: if we have already converted this exact Rc allocation,
// reuse the cached array-literal index and element type.
let (const_array_id, array_elem_ty) =
if let Some(&(id, ty)) = self.array_literal_cache.get(&array_ptr) {
(id, ty)
} else {
// Slow path: convert the array to a literal and deduplicate.
let array_literal =
convert_to_array_literal(array, array_package_span, index_package_span)?;
let elem_ty = array_literal.ty;
let id = if let Some(idx) = self
.program
.array_literals
.iter()
.position(|a| a == &array_literal)
{
idx
} else {
let idx = self.program.array_literals.len();
self.program.array_literals.push(array_literal);
idx
};
self.array_literal_cache.insert(array_ptr, (id, elem_ty));
(id, elem_ty)
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: just added the last two lines to the suggestion, was missing pushing to the cache and returning the tuple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants