hugr_llvm/extension/collections/
stack_array.rs

1//! Codegen for prelude array operations.
2//!
3//! Allocates arrays on the stack and passes them around as LLVM values. This implementation is
4//! deprecated in favour of the lowering in [crate::extension::collections::array].
5
6#![allow(deprecated)]
7
8use std::iter;
9
10use anyhow::{Ok, Result, anyhow};
11use hugr_core::extension::prelude::option_type;
12use hugr_core::extension::simple_op::{MakeExtensionOp, MakeRegisteredOp};
13use hugr_core::ops::DataflowOpTrait;
14use hugr_core::std_extensions::collections::array::{
15    self, ArrayOp, ArrayOpDef, ArrayRepeat, ArrayScan, array_type,
16};
17use hugr_core::types::{TypeArg, TypeEnum};
18use hugr_core::{HugrView, Node};
19use inkwell::IntPredicate;
20use inkwell::builder::{Builder, BuilderError};
21use inkwell::types::{BasicType, BasicTypeEnum};
22use inkwell::values::{
23    ArrayValue, BasicValue as _, BasicValueEnum, CallableValue, IntValue, PointerValue,
24};
25use itertools::Itertools;
26
27use crate::emit::emit_value;
28use crate::{CodegenExtension, CodegenExtsBuilder};
29use crate::{
30    emit::{EmitFuncContext, RowPromise, deaggregate_call_result},
31    types::{HugrType, TypingSession},
32};
33
34/// A helper trait for customising the lowering of [hugr_core::std_extensions::collections::array]
35/// types, [hugr_core::ops::constant::CustomConst]s, and ops.
36#[deprecated(
37    note = "Use `hugr_llvm::extension::collections::array` instead",
38    since = "0.20.0"
39)]
40pub trait ArrayCodegen: Clone {
41    /// Return the llvm type of [hugr_core::std_extensions::collections::array::ARRAY_TYPENAME].
42    fn array_type<'c>(
43        &self,
44        _session: &TypingSession<'c, '_>,
45        elem_ty: BasicTypeEnum<'c>,
46        size: u64,
47    ) -> impl BasicType<'c> {
48        elem_ty.array_type(size as u32)
49    }
50
51    /// Emit a [hugr_core::std_extensions::collections::array::ArrayValue].
52    fn emit_array_value<'c, H: HugrView<Node = Node>>(
53        &self,
54        ctx: &mut EmitFuncContext<'c, '_, H>,
55        value: &array::ArrayValue,
56    ) -> Result<BasicValueEnum<'c>> {
57        emit_array_value(self, ctx, value)
58    }
59
60    /// Emit a [hugr_core::std_extensions::collections::array::ArrayOp].
61    fn emit_array_op<'c, H: HugrView<Node = Node>>(
62        &self,
63        ctx: &mut EmitFuncContext<'c, '_, H>,
64        op: ArrayOp,
65        inputs: Vec<BasicValueEnum<'c>>,
66        outputs: RowPromise<'c>,
67    ) -> Result<()> {
68        emit_array_op(self, ctx, op, inputs, outputs)
69    }
70
71    /// Emit a [hugr_core::std_extensions::collections::array::ArrayRepeat] op.
72    fn emit_array_repeat<'c, H: HugrView<Node = Node>>(
73        &self,
74        ctx: &mut EmitFuncContext<'c, '_, H>,
75        op: ArrayRepeat,
76        func: BasicValueEnum<'c>,
77    ) -> Result<BasicValueEnum<'c>> {
78        emit_repeat_op(ctx, op, func)
79    }
80
81    /// Emit a [hugr_core::std_extensions::collections::array::ArrayScan] op.
82    ///
83    /// Returns the resulting array and the final values of the accumulators.
84    fn emit_array_scan<'c, H: HugrView<Node = Node>>(
85        &self,
86        ctx: &mut EmitFuncContext<'c, '_, H>,
87        op: ArrayScan,
88        src_array: BasicValueEnum<'c>,
89        func: BasicValueEnum<'c>,
90        initial_accs: &[BasicValueEnum<'c>],
91    ) -> Result<(BasicValueEnum<'c>, Vec<BasicValueEnum<'c>>)> {
92        emit_scan_op(ctx, op, src_array, func, initial_accs)
93    }
94}
95
96/// A trivial implementation of [ArrayCodegen] which passes all methods
97/// through to their default implementations.
98#[deprecated(
99    note = "Use `hugr_llvm::extension::collections::array` instead",
100    since = "0.20.0"
101)]
102#[derive(Default, Clone)]
103pub struct DefaultArrayCodegen;
104
105impl ArrayCodegen for DefaultArrayCodegen {}
106
107#[deprecated(
108    note = "Use `hugr_llvm::extension::collections::array` instead",
109    since = "0.20.0"
110)]
111#[derive(Clone, Debug, Default)]
112pub struct ArrayCodegenExtension<CCG>(CCG);
113
114impl<CCG: ArrayCodegen> ArrayCodegenExtension<CCG> {
115    pub fn new(ccg: CCG) -> Self {
116        Self(ccg)
117    }
118}
119
120impl<CCG: ArrayCodegen> From<CCG> for ArrayCodegenExtension<CCG> {
121    fn from(ccg: CCG) -> Self {
122        Self::new(ccg)
123    }
124}
125
126impl<CCG: ArrayCodegen> CodegenExtension for ArrayCodegenExtension<CCG> {
127    fn add_extension<'a, H: HugrView<Node = Node> + 'a>(
128        self,
129        builder: CodegenExtsBuilder<'a, H>,
130    ) -> CodegenExtsBuilder<'a, H>
131    where
132        Self: 'a,
133    {
134        builder
135            .custom_type((array::EXTENSION_ID, array::ARRAY_TYPENAME), {
136                let ccg = self.0.clone();
137                move |ts, hugr_type| {
138                    let [TypeArg::BoundedNat(n), TypeArg::Runtime(ty)] = hugr_type.args() else {
139                        return Err(anyhow!("Invalid type args for array type"));
140                    };
141                    let elem_ty = ts.llvm_type(ty)?;
142                    Ok(ccg.array_type(&ts, elem_ty, *n).as_basic_type_enum())
143                }
144            })
145            .custom_const::<array::ArrayValue>({
146                let ccg = self.0.clone();
147                move |context, k| ccg.emit_array_value(context, k)
148            })
149            .simple_extension_op::<ArrayOpDef>({
150                let ccg = self.0.clone();
151                move |context, args, _| {
152                    ccg.emit_array_op(
153                        context,
154                        ArrayOp::from_extension_op(args.node().as_ref())?,
155                        args.inputs,
156                        args.outputs,
157                    )
158                }
159            })
160            .extension_op(array::EXTENSION_ID, array::ARRAY_REPEAT_OP_ID, {
161                let ccg = self.0.clone();
162                move |context, args| {
163                    let func = args.inputs[0];
164                    let op = ArrayRepeat::from_extension_op(args.node().as_ref())?;
165                    let arr = ccg.emit_array_repeat(context, op, func)?;
166                    args.outputs.finish(context.builder(), [arr])
167                }
168            })
169            .extension_op(array::EXTENSION_ID, array::ARRAY_SCAN_OP_ID, {
170                let ccg = self.0.clone();
171                move |context, args| {
172                    let src_array = args.inputs[0];
173                    let func = args.inputs[1];
174                    let initial_accs = &args.inputs[2..];
175                    let op = ArrayScan::from_extension_op(args.node().as_ref())?;
176                    let (tgt_array, final_accs) =
177                        ccg.emit_array_scan(context, op, src_array, func, initial_accs)?;
178                    args.outputs
179                        .finish(context.builder(), iter::once(tgt_array).chain(final_accs))
180                }
181            })
182            .extension_op(
183                array::EXTENSION_ID,
184                array::ARRAY_CLONE_OP_ID,
185                |context, args| {
186                    args.outputs
187                        .finish(context.builder(), vec![args.inputs[0]; 2])
188                },
189            )
190            .extension_op(
191                array::EXTENSION_ID,
192                array::ARRAY_DISCARD_OP_ID,
193                |context, args| args.outputs.finish(context.builder(), []),
194            )
195    }
196}
197
198/// Helper function to allocate an array on the stack.
199///
200/// Returns two pointers: The first one is a pointer to the first element of the
201/// array (i.e. it is of type `array.get_element_type().ptr_type()`) whereas the
202/// second one points to the whole array value, i.e. it is of type `array.ptr_type()`.
203fn build_array_alloca<'c>(
204    builder: &Builder<'c>,
205    array: ArrayValue<'c>,
206) -> Result<(PointerValue<'c>, PointerValue<'c>), BuilderError> {
207    let array_ty = array.get_type();
208    let array_len: IntValue<'c> = {
209        let ctx = builder.get_insert_block().unwrap().get_context();
210        ctx.i32_type().const_int(array_ty.len() as u64, false)
211    };
212    let ptr = builder.build_array_alloca(array_ty.get_element_type(), array_len, "")?;
213    let array_ptr = builder
214        .build_bit_cast(ptr, array_ty.ptr_type(Default::default()), "")?
215        .into_pointer_value();
216    builder.build_store(array_ptr, array)?;
217    Result::Ok((ptr, array_ptr))
218}
219
220/// Helper function to allocate an array on the stack and pass a pointer to it
221/// to a closure.
222///
223/// The pointer forwarded to the closure is a pointer to the first element of
224/// the array. I.e. it is of type `array.get_element_type().ptr_type()` not
225/// `array.ptr_type()`
226fn with_array_alloca<'c, T, E: From<BuilderError>>(
227    builder: &Builder<'c>,
228    array: ArrayValue<'c>,
229    go: impl FnOnce(PointerValue<'c>) -> Result<T, E>,
230) -> Result<T, E> {
231    let (ptr, _) = build_array_alloca(builder, array)?;
232    go(ptr)
233}
234
235/// Helper function to build a loop that repeats for a given number of iterations.
236///
237/// The provided closure is called to build the loop body. Afterwards, the builder is positioned at
238/// the end of the loop exit block.
239fn build_loop<'c, T, H: HugrView<Node = Node>>(
240    ctx: &mut EmitFuncContext<'c, '_, H>,
241    iters: IntValue<'c>,
242    go: impl FnOnce(&mut EmitFuncContext<'c, '_, H>, IntValue<'c>) -> Result<T>,
243) -> Result<T> {
244    let builder = ctx.builder();
245    let idx_ty = ctx.iw_context().i32_type();
246    let idx_ptr = builder.build_alloca(idx_ty, "")?;
247    builder.build_store(idx_ptr, idx_ty.const_zero())?;
248
249    let exit_block = ctx.new_basic_block("", None);
250
251    let (body_block, val) = ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
252        let idx = ctx.builder().build_load(idx_ptr, "")?.into_int_value();
253        let val = go(ctx, idx)?;
254        let builder = ctx.builder();
255        let inc_idx = builder.build_int_add(idx, idx_ty.const_int(1, false), "")?;
256        builder.build_store(idx_ptr, inc_idx)?;
257        // Branch to the head is built later
258        Ok((bb, val))
259    })?;
260
261    let head_block = ctx.build_positioned_new_block("", Some(body_block), |ctx, bb| {
262        let builder = ctx.builder();
263        let idx = builder.build_load(idx_ptr, "")?.into_int_value();
264        let cmp = builder.build_int_compare(IntPredicate::ULT, idx, iters, "")?;
265        builder.build_conditional_branch(cmp, body_block, exit_block)?;
266        Ok(bb)
267    })?;
268
269    let builder = ctx.builder();
270    builder.build_unconditional_branch(head_block)?;
271    builder.position_at_end(body_block);
272    builder.build_unconditional_branch(head_block)?;
273    ctx.builder().position_at_end(exit_block);
274    Ok(val)
275}
276
277fn emit_array_value<'c, H: HugrView<Node = Node>>(
278    ccg: &impl ArrayCodegen,
279    ctx: &mut EmitFuncContext<'c, '_, H>,
280    value: &array::ArrayValue,
281) -> Result<BasicValueEnum<'c>> {
282    let ts = ctx.typing_session();
283    let llvm_array_ty = ccg
284        .array_type(
285            &ts,
286            ts.llvm_type(value.get_element_type())?,
287            value.get_contents().len() as u64,
288        )
289        .as_basic_type_enum()
290        .into_array_type();
291    let mut array_v = llvm_array_ty.get_undef();
292    for (i, v) in value.get_contents().iter().enumerate() {
293        let llvm_v = emit_value(ctx, v)?;
294        array_v = ctx
295            .builder()
296            .build_insert_value(array_v, llvm_v, i as u32, "")?
297            .into_array_value();
298    }
299    Ok(array_v.into())
300}
301
302fn emit_array_op<'c, H: HugrView<Node = Node>>(
303    ccg: &impl ArrayCodegen,
304    ctx: &mut EmitFuncContext<'c, '_, H>,
305    op: ArrayOp,
306    inputs: Vec<BasicValueEnum<'c>>,
307    outputs: RowPromise<'c>,
308) -> Result<()> {
309    let builder = ctx.builder();
310    let ts = ctx.typing_session();
311    let sig = op
312        .clone()
313        .to_extension_op()
314        .unwrap()
315        .signature()
316        .into_owned();
317    let ArrayOp {
318        def,
319        ref elem_ty,
320        size,
321    } = op;
322    let llvm_array_ty = ccg
323        .array_type(&ts, ts.llvm_type(elem_ty)?, size)
324        .as_basic_type_enum()
325        .into_array_type();
326    match def {
327        ArrayOpDef::new_array => {
328            let mut array_v = llvm_array_ty.get_undef();
329            for (i, v) in inputs.into_iter().enumerate() {
330                array_v = builder
331                    .build_insert_value(array_v, v, i as u32, "")?
332                    .into_array_value();
333            }
334            outputs.finish(builder, [array_v.as_basic_value_enum()])
335        }
336        ArrayOpDef::unpack => {
337            let [array_v] = inputs
338                .try_into()
339                .map_err(|_| anyhow!("ArrayOpDef::unpack expects one argument"))?;
340            let array_v = array_v.into_array_value();
341
342            let result = (0..size)
343                .map(|i| builder.build_extract_value(array_v, i as u32, "extract"))
344                .collect::<Result<Vec<_>, _>>()?;
345
346            outputs.finish(builder, result)
347        }
348        ArrayOpDef::get => {
349            let [array_v, index_v] = inputs
350                .try_into()
351                .map_err(|_| anyhow!("ArrayOpDef::get expects two arguments"))?;
352            let array_v = array_v.into_array_value();
353            let index_v = index_v.into_int_value();
354            let res_hugr_ty = sig
355                .output()
356                .get(0)
357                .ok_or(anyhow!("ArrayOp::get has no outputs"))?;
358
359            let res_sum_ty = {
360                let TypeEnum::Sum(st) = res_hugr_ty.as_type_enum() else {
361                    Err(anyhow!("ArrayOp::get output is not a sum type"))?
362                };
363                ts.llvm_sum_type(st.clone())?
364            };
365
366            let exit_rmb = ctx.new_row_mail_box(sig.output().iter(), "")?;
367
368            let exit_block = ctx.build_positioned_new_block("", None, |ctx, bb| {
369                outputs.finish(ctx.builder(), exit_rmb.read_vec(ctx.builder(), [])?)?;
370                Ok(bb)
371            })?;
372
373            let success_block =
374                ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
375                    let builder = ctx.builder();
376                    let elem_v = with_array_alloca(builder, array_v, |ptr| {
377                        // inside `success_block` we know `index_v` to be in
378                        // bounds.
379                        let elem_addr =
380                            unsafe { builder.build_in_bounds_gep(ptr, &[index_v], "")? };
381                        builder.build_load(elem_addr, "")
382                    })?;
383                    let success_v = res_sum_ty.build_tag(builder, 1, vec![elem_v])?;
384                    exit_rmb.write(ctx.builder(), [success_v.into(), array_v.into()])?;
385                    builder.build_unconditional_branch(exit_block)?;
386                    Ok(bb)
387                })?;
388
389            let failure_block =
390                ctx.build_positioned_new_block("", Some(success_block), |ctx, bb| {
391                    let builder = ctx.builder();
392                    let failure_v = res_sum_ty.build_tag(builder, 0, vec![])?;
393                    exit_rmb.write(ctx.builder(), [failure_v.into(), array_v.into()])?;
394                    builder.build_unconditional_branch(exit_block)?;
395                    Ok(bb)
396                })?;
397
398            let builder = ctx.builder();
399            let is_success = builder.build_int_compare(
400                IntPredicate::ULT,
401                index_v,
402                index_v.get_type().const_int(size, false),
403                "",
404            )?;
405
406            builder.build_conditional_branch(is_success, success_block, failure_block)?;
407            builder.position_at_end(exit_block);
408            Ok(())
409        }
410        ArrayOpDef::set => {
411            let [array_v0, index_v, value_v] = inputs
412                .try_into()
413                .map_err(|_| anyhow!("ArrayOpDef::set expects three arguments"))?;
414            let array_v = array_v0.into_array_value();
415            let index_v = index_v.into_int_value();
416
417            let res_hugr_ty = sig
418                .output()
419                .get(0)
420                .ok_or(anyhow!("ArrayOp::set has no outputs"))?;
421
422            let res_sum_ty = {
423                let TypeEnum::Sum(st) = res_hugr_ty.as_type_enum() else {
424                    Err(anyhow!("ArrayOp::set output is not a sum type"))?
425                };
426                ts.llvm_sum_type(st.clone())?
427            };
428
429            let exit_rmb = ctx.new_row_mail_box([res_hugr_ty], "")?;
430
431            let exit_block = ctx.build_positioned_new_block("", None, |ctx, bb| {
432                outputs.finish(ctx.builder(), exit_rmb.read_vec(ctx.builder(), [])?)?;
433                Ok(bb)
434            })?;
435
436            let success_block =
437                ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
438                    let builder = ctx.builder();
439                    let (elem_v, array_v) = with_array_alloca(builder, array_v, |ptr| {
440                        // inside `success_block` we know `index_v` to be in
441                        // bounds.
442                        let elem_addr =
443                            unsafe { builder.build_in_bounds_gep(ptr, &[index_v], "")? };
444                        let elem_v = builder.build_load(elem_addr, "")?;
445                        builder.build_store(elem_addr, value_v)?;
446                        let ptr = builder
447                            .build_bit_cast(
448                                ptr,
449                                array_v.get_type().ptr_type(Default::default()),
450                                "",
451                            )?
452                            .into_pointer_value();
453                        let array_v = builder.build_load(ptr, "")?;
454                        Ok((elem_v, array_v))
455                    })?;
456                    let success_v = res_sum_ty.build_tag(builder, 1, vec![elem_v, array_v])?;
457                    exit_rmb.write(ctx.builder(), [success_v.into()])?;
458                    builder.build_unconditional_branch(exit_block)?;
459                    Ok(bb)
460                })?;
461
462            let failure_block =
463                ctx.build_positioned_new_block("", Some(success_block), |ctx, bb| {
464                    let builder = ctx.builder();
465                    let failure_v =
466                        res_sum_ty.build_tag(builder, 0, vec![value_v, array_v.into()])?;
467                    exit_rmb.write(ctx.builder(), [failure_v.into()])?;
468                    builder.build_unconditional_branch(exit_block)?;
469                    Ok(bb)
470                })?;
471
472            let builder = ctx.builder();
473            let is_success = builder.build_int_compare(
474                IntPredicate::ULT,
475                index_v,
476                index_v.get_type().const_int(size, false),
477                "",
478            )?;
479            builder.build_conditional_branch(is_success, success_block, failure_block)?;
480            builder.position_at_end(exit_block);
481            Ok(())
482        }
483        ArrayOpDef::swap => {
484            let [array_v0, index1_v, index2_v] = inputs
485                .try_into()
486                .map_err(|_| anyhow!("ArrayOpDef::swap expects three arguments"))?;
487            let array_v = array_v0.into_array_value();
488            let index1_v = index1_v.into_int_value();
489            let index2_v = index2_v.into_int_value();
490
491            let res_hugr_ty = sig
492                .output()
493                .get(0)
494                .ok_or(anyhow!("ArrayOp::swap has no outputs"))?;
495
496            let res_sum_ty = {
497                let TypeEnum::Sum(st) = res_hugr_ty.as_type_enum() else {
498                    Err(anyhow!("ArrayOp::swap output is not a sum type"))?
499                };
500                ts.llvm_sum_type(st.clone())?
501            };
502
503            let exit_rmb = ctx.new_row_mail_box([res_hugr_ty], "")?;
504
505            let exit_block = ctx.build_positioned_new_block("", None, |ctx, bb| {
506                outputs.finish(ctx.builder(), exit_rmb.read_vec(ctx.builder(), [])?)?;
507                Ok(bb)
508            })?;
509
510            let success_block =
511                ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
512                    // if `index1_v` == `index2_v` then the following is a no-op.
513                    // We could check for this: either with a select instruction
514                    // here, or by branching to another case in earlier.
515                    // Doing so would generate better code in cases where the
516                    // optimiser can determine that the indices are the same, at
517                    // the cost of worse code in cases where it cannot.
518                    // For now we choose the simpler option of omitting the check.
519                    let builder = ctx.builder();
520                    let array_v = with_array_alloca(builder, array_v, |ptr| {
521                        // inside `success_block` we know `index1_v` and `index2_v`
522                        // to be in bounds.
523                        let elem1_addr =
524                            unsafe { builder.build_in_bounds_gep(ptr, &[index1_v], "")? };
525                        let elem1_v = builder.build_load(elem1_addr, "")?;
526                        let elem2_addr =
527                            unsafe { builder.build_in_bounds_gep(ptr, &[index2_v], "")? };
528                        let elem2_v = builder.build_load(elem2_addr, "")?;
529                        builder.build_store(elem1_addr, elem2_v)?;
530                        builder.build_store(elem2_addr, elem1_v)?;
531                        let ptr = builder
532                            .build_bit_cast(
533                                ptr,
534                                array_v.get_type().ptr_type(Default::default()),
535                                "",
536                            )?
537                            .into_pointer_value();
538                        builder.build_load(ptr, "")
539                    })?;
540                    let success_v = res_sum_ty.build_tag(builder, 1, vec![array_v])?;
541                    exit_rmb.write(ctx.builder(), [success_v.into()])?;
542                    builder.build_unconditional_branch(exit_block)?;
543                    Ok(bb)
544                })?;
545
546            let failure_block =
547                ctx.build_positioned_new_block("", Some(success_block), |ctx, bb| {
548                    let builder = ctx.builder();
549                    let failure_v = res_sum_ty.build_tag(builder, 0, vec![array_v.into()])?;
550                    exit_rmb.write(ctx.builder(), [failure_v.into()])?;
551                    builder.build_unconditional_branch(exit_block)?;
552                    Ok(bb)
553                })?;
554
555            let builder = ctx.builder();
556            let is_success = {
557                let index1_ok = builder.build_int_compare(
558                    IntPredicate::ULT,
559                    index1_v,
560                    index1_v.get_type().const_int(size, false),
561                    "",
562                )?;
563                let index2_ok = builder.build_int_compare(
564                    IntPredicate::ULT,
565                    index2_v,
566                    index2_v.get_type().const_int(size, false),
567                    "",
568                )?;
569                builder.build_and(index1_ok, index2_ok, "")?
570            };
571            builder.build_conditional_branch(is_success, success_block, failure_block)?;
572            builder.position_at_end(exit_block);
573            Ok(())
574        }
575        ArrayOpDef::pop_left => {
576            let [array_v] = inputs
577                .try_into()
578                .map_err(|_| anyhow!("ArrayOpDef::pop_left expects one argument"))?;
579            let r = emit_pop_op(
580                builder,
581                &ts,
582                elem_ty.clone(),
583                size,
584                array_v.into_array_value(),
585                true,
586            )?;
587            outputs.finish(ctx.builder(), [r])
588        }
589        ArrayOpDef::pop_right => {
590            let [array_v] = inputs
591                .try_into()
592                .map_err(|_| anyhow!("ArrayOpDef::pop_right expects one argument"))?;
593            let r = emit_pop_op(
594                builder,
595                &ts,
596                elem_ty.clone(),
597                size,
598                array_v.into_array_value(),
599                false,
600            )?;
601            outputs.finish(ctx.builder(), [r])
602        }
603        ArrayOpDef::discard_empty => Ok(()),
604        _ => todo!(),
605    }
606}
607
608/// Helper function to emit the pop operations.
609fn emit_pop_op<'c>(
610    builder: &Builder<'c>,
611    ts: &TypingSession<'c, '_>,
612    elem_ty: HugrType,
613    size: u64,
614    array_v: ArrayValue<'c>,
615    pop_left: bool,
616) -> Result<BasicValueEnum<'c>> {
617    let ret_ty = ts.llvm_sum_type(option_type(vec![
618        elem_ty.clone(),
619        array_type(size.saturating_add_signed(-1), elem_ty),
620    ]))?;
621    if size == 0 {
622        return Ok(ret_ty.build_tag(builder, 0, vec![])?.into());
623    }
624    let ctx = builder.get_insert_block().unwrap().get_context();
625    let (elem_v, array_v) = with_array_alloca(builder, array_v, |ptr| {
626        let (elem_ptr, ptr) = {
627            if pop_left {
628                let rest_ptr =
629                    unsafe { builder.build_gep(ptr, &[ctx.i32_type().const_int(1, false)], "") }?;
630                (ptr, rest_ptr)
631            } else {
632                let elem_ptr = unsafe {
633                    builder.build_gep(ptr, &[ctx.i32_type().const_int(size - 1, false)], "")
634                }?;
635                (elem_ptr, ptr)
636            }
637        };
638        let elem_v = builder.build_load(elem_ptr, "")?;
639        let new_array_ty = array_v
640            .get_type()
641            .get_element_type()
642            .array_type(size as u32 - 1);
643        let ptr = builder
644            .build_bit_cast(ptr, new_array_ty.ptr_type(Default::default()), "")?
645            .into_pointer_value();
646        let array_v = builder.build_load(ptr, "")?;
647        Ok((elem_v, array_v))
648    })?;
649    Ok(ret_ty.build_tag(builder, 1, vec![elem_v, array_v])?.into())
650}
651
652/// Emits an [ArrayRepeat] op.
653fn emit_repeat_op<'c, H: HugrView<Node = Node>>(
654    ctx: &mut EmitFuncContext<'c, '_, H>,
655    op: ArrayRepeat,
656    func: BasicValueEnum<'c>,
657) -> Result<BasicValueEnum<'c>> {
658    let builder = ctx.builder();
659    let array_len = ctx.iw_context().i32_type().const_int(op.size, false);
660    let array_ty = ctx.llvm_type(&op.elem_ty)?.array_type(op.size as u32);
661    let (ptr, array_ptr) = build_array_alloca(builder, array_ty.get_undef())?;
662    build_loop(ctx, array_len, |ctx, idx| {
663        let builder = ctx.builder();
664        let func_ptr = CallableValue::try_from(func.into_pointer_value())
665            .map_err(|_| anyhow!("ArrayOpDef::repeat expects a function pointer"))?;
666        let v = builder
667            .build_call(func_ptr, &[], "")?
668            .try_as_basic_value()
669            .left()
670            .ok_or(anyhow!("ArrayOpDef::repeat function must return a value"))?;
671        let elem_addr = unsafe { builder.build_in_bounds_gep(ptr, &[idx], "")? };
672        builder.build_store(elem_addr, v)?;
673        Ok(())
674    })?;
675
676    let builder = ctx.builder();
677    let array_v = builder.build_load(array_ptr, "")?;
678    Ok(array_v)
679}
680
681/// Emits an [ArrayScan] op.
682///
683/// Returns the resulting array and the final values of the accumulators.
684fn emit_scan_op<'c, H: HugrView<Node = Node>>(
685    ctx: &mut EmitFuncContext<'c, '_, H>,
686    op: ArrayScan,
687    src_array: BasicValueEnum<'c>,
688    func: BasicValueEnum<'c>,
689    initial_accs: &[BasicValueEnum<'c>],
690) -> Result<(BasicValueEnum<'c>, Vec<BasicValueEnum<'c>>)> {
691    let builder = ctx.builder();
692    let ts = ctx.typing_session();
693    let array_len = ctx.iw_context().i32_type().const_int(op.size, false);
694    let tgt_array_ty = ts.llvm_type(&op.tgt_ty)?.array_type(op.size as u32);
695    let (src_ptr, _) = build_array_alloca(builder, src_array.into_array_value())?;
696    let (tgt_ptr, tgt_array_ptr) = build_array_alloca(builder, tgt_array_ty.get_undef())?;
697
698    let acc_tys: Vec<_> = op.acc_tys.iter().map(|ty| ts.llvm_type(ty)).try_collect()?;
699    let acc_ptrs: Vec<_> = acc_tys
700        .iter()
701        .map(|ty| builder.build_alloca(*ty, ""))
702        .try_collect()?;
703    for (ptr, initial_val) in acc_ptrs.iter().zip(initial_accs) {
704        builder.build_store(*ptr, *initial_val)?;
705    }
706
707    build_loop(ctx, array_len, |ctx, idx| {
708        let builder = ctx.builder();
709        let func_ptr = CallableValue::try_from(func.into_pointer_value())
710            .map_err(|_| anyhow!("ArrayOpDef::scan expects a function pointer"))?;
711        let src_elem_addr = unsafe { builder.build_in_bounds_gep(src_ptr, &[idx], "")? };
712        let src_elem = builder.build_load(src_elem_addr, "")?;
713        let mut args = vec![src_elem.into()];
714        for ptr in acc_ptrs.iter() {
715            args.push(builder.build_load(*ptr, "")?.into());
716        }
717        let call = builder.build_call(func_ptr, args.as_slice(), "")?;
718        let call_results = deaggregate_call_result(builder, call, 1 + acc_tys.len())?;
719        let tgt_elem_addr = unsafe { builder.build_in_bounds_gep(tgt_ptr, &[idx], "")? };
720        builder.build_store(tgt_elem_addr, call_results[0])?;
721        for (ptr, next_act) in acc_ptrs.iter().zip(call_results[1..].iter()) {
722            builder.build_store(*ptr, *next_act)?;
723        }
724        Ok(())
725    })?;
726
727    let builder = ctx.builder();
728    let tgt_array_v = builder.build_load(tgt_array_ptr, "")?;
729    let final_accs = acc_ptrs
730        .into_iter()
731        .map(|ptr| builder.build_load(ptr, ""))
732        .try_collect()?;
733    Ok((tgt_array_v, final_accs))
734}
735
736#[cfg(test)]
737mod test {
738    use hugr_core::builder::{DataflowHugr as _, HugrBuilder};
739    use hugr_core::extension::prelude::either_type;
740    use hugr_core::ops::Tag;
741    use hugr_core::std_extensions::STD_REG;
742    use hugr_core::std_extensions::collections::array::op_builder::build_all_array_ops;
743    use hugr_core::std_extensions::collections::array::{self, ArrayRepeat, ArrayScan, array_type};
744    use hugr_core::types::Type;
745    use hugr_core::{
746        builder::{Dataflow, DataflowSubContainer, SubContainer},
747        extension::{
748            ExtensionRegistry,
749            prelude::{self, ConstUsize, UnwrapBuilder as _, bool_t, option_type, usize_t},
750        },
751        ops::Value,
752        std_extensions::{
753            arithmetic::{
754                int_ops::{self},
755                int_types::{self, ConstInt, int_type},
756            },
757            collections::array::ArrayOpBuilder,
758            logic,
759        },
760        type_row,
761        types::Signature,
762    };
763    use itertools::Itertools as _;
764    use rstest::rstest;
765
766    use crate::extension::collections::stack_array::{ArrayCodegenExtension, DefaultArrayCodegen};
767    use crate::{
768        check_emission,
769        emit::test::SimpleHugrConfig,
770        test::{TestContext, exec_ctx, llvm_ctx},
771        utils::{IntOpBuilder, LogicOpBuilder},
772    };
773
774    #[rstest]
775    fn emit_all_ops(mut llvm_ctx: TestContext) {
776        let hugr = SimpleHugrConfig::new()
777            .with_extensions(STD_REG.to_owned())
778            .finish(|mut builder| {
779                build_all_array_ops(builder.dfg_builder_endo([]).unwrap())
780                    .finish_sub_container()
781                    .unwrap();
782                builder.finish_hugr().unwrap()
783            });
784        llvm_ctx.add_extensions(|cge| {
785            cge.add_default_prelude_extensions()
786                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
787        });
788        check_emission!(hugr, llvm_ctx);
789    }
790
791    #[rstest]
792    fn emit_get(mut llvm_ctx: TestContext) {
793        let hugr = SimpleHugrConfig::new()
794            .with_extensions(STD_REG.to_owned())
795            .finish(|mut builder| {
796                let us1 = builder.add_load_value(ConstUsize::new(1));
797                let us2 = builder.add_load_value(ConstUsize::new(2));
798                let arr = builder.add_new_array(usize_t(), [us1, us2]).unwrap();
799                let (_, arr) = builder.add_array_get(usize_t(), 2, arr, us1).unwrap();
800                builder.add_array_discard(usize_t(), 2, arr).unwrap();
801                builder.finish_hugr_with_outputs([]).unwrap()
802            });
803        llvm_ctx.add_extensions(|cge| {
804            cge.add_default_prelude_extensions()
805                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
806        });
807        check_emission!(hugr, llvm_ctx);
808    }
809
810    #[rstest]
811    fn emit_clone(mut llvm_ctx: TestContext) {
812        let hugr = SimpleHugrConfig::new()
813            .with_extensions(STD_REG.to_owned())
814            .finish(|mut builder| {
815                let us1 = builder.add_load_value(ConstUsize::new(1));
816                let us2 = builder.add_load_value(ConstUsize::new(2));
817                let arr = builder.add_new_array(usize_t(), [us1, us2]).unwrap();
818                let (arr1, arr2) = builder.add_array_clone(usize_t(), 2, arr).unwrap();
819                builder.add_array_discard(usize_t(), 2, arr1).unwrap();
820                builder.add_array_discard(usize_t(), 2, arr2).unwrap();
821                builder.finish_hugr_with_outputs([]).unwrap()
822            });
823        llvm_ctx.add_extensions(|cge| {
824            cge.add_default_prelude_extensions()
825                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
826        });
827        check_emission!(hugr, llvm_ctx);
828    }
829
830    #[rstest]
831    fn emit_array_value(mut llvm_ctx: TestContext) {
832        let hugr = SimpleHugrConfig::new()
833            .with_extensions(STD_REG.to_owned())
834            .with_outs(vec![array_type(2, usize_t())])
835            .finish(|mut builder| {
836                let vs = vec![ConstUsize::new(1).into(), ConstUsize::new(2).into()];
837                let arr = builder.add_load_value(array::ArrayValue::new(usize_t(), vs));
838                builder.finish_hugr_with_outputs([arr]).unwrap()
839            });
840        llvm_ctx.add_extensions(|cge| {
841            cge.add_default_prelude_extensions()
842                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
843        });
844        check_emission!(hugr, llvm_ctx);
845    }
846
847    fn exec_registry() -> ExtensionRegistry {
848        ExtensionRegistry::new([
849            int_types::EXTENSION.to_owned(),
850            int_ops::EXTENSION.to_owned(),
851            logic::EXTENSION.to_owned(),
852            prelude::PRELUDE.to_owned(),
853            array::EXTENSION.to_owned(),
854        ])
855    }
856
857    #[rstest]
858    #[case(0, 1)]
859    #[case(1, 2)]
860    #[case(3, 0)]
861    #[case(999999, 0)]
862    fn exec_get(mut exec_ctx: TestContext, #[case] index: u64, #[case] expected: u64) {
863        // We build a HUGR that:
864        // - Creates an array of [1,2]
865        // - Gets the element at the given index
866        // - Returns the element if the index is in bounds, otherwise 0
867        let hugr = SimpleHugrConfig::new()
868            .with_outs(usize_t())
869            .with_extensions(exec_registry())
870            .finish(|mut builder| {
871                let us0 = builder.add_load_value(ConstUsize::new(0));
872                let us1 = builder.add_load_value(ConstUsize::new(1));
873                let us2 = builder.add_load_value(ConstUsize::new(2));
874                let arr = builder.add_new_array(usize_t(), [us1, us2]).unwrap();
875                let i = builder.add_load_value(ConstUsize::new(index));
876                let (get_r, arr) = builder.add_array_get(usize_t(), 2, arr, i).unwrap();
877                builder.add_array_discard(usize_t(), 2, arr).unwrap();
878                let r = {
879                    let ot = option_type(usize_t());
880                    let variants = (0..ot.num_variants())
881                        .map(|i| ot.get_variant(i).cloned().unwrap().try_into().unwrap())
882                        .collect_vec();
883                    let mut builder = builder
884                        .conditional_builder((variants, get_r), [], usize_t().into())
885                        .unwrap();
886                    {
887                        let failure_case = builder.case_builder(0).unwrap();
888                        failure_case.finish_with_outputs([us0]).unwrap();
889                    }
890                    {
891                        let success_case = builder.case_builder(1).unwrap();
892                        let inputs = success_case.input_wires();
893                        success_case.finish_with_outputs(inputs).unwrap();
894                    }
895                    builder.finish_sub_container().unwrap().out_wire(0)
896                };
897                builder.finish_hugr_with_outputs([r]).unwrap()
898            });
899        exec_ctx.add_extensions(|cge| {
900            cge.add_default_prelude_extensions()
901                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
902        });
903        assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
904    }
905
906    #[rstest]
907    #[case(0, 3, 1, [3,2])]
908    #[case(1, 3, 2, [1,3])]
909    #[case(2, 3, 3, [1,2])]
910    #[case(999999, 3, 3, [1,2])]
911    fn exec_set(
912        mut exec_ctx: TestContext,
913        #[case] index: u64,
914        #[case] value: u64,
915        #[case] expected_elem: u64,
916        #[case] expected_arr: [u64; 2],
917    ) {
918        // We build a HUGR that
919        // - Creates an array: [1,2]
920        // - Sets the element at the given index to the given value
921        // - Checks the following, returning 1 iff they are all true:
922        //   - The element returned from set is `expected_elem`
923        //   - The Oth element of the resulting array is `expected_arr_0`
924
925        use hugr_core::extension::prelude::either_type;
926        let int_ty = int_type(3);
927        let hugr = SimpleHugrConfig::new()
928            .with_outs(usize_t())
929            .with_extensions(exec_registry())
930            .finish(|mut builder| {
931                let us0 = builder.add_load_value(ConstUsize::new(0));
932                let us1 = builder.add_load_value(ConstUsize::new(1));
933                let i1 = builder.add_load_value(ConstInt::new_u(3, 1).unwrap());
934                let i2 = builder.add_load_value(ConstInt::new_u(3, 2).unwrap());
935                let arr = builder.add_new_array(int_ty.clone(), [i1, i2]).unwrap();
936                let index = builder.add_load_value(ConstUsize::new(index));
937                let value = builder.add_load_value(ConstInt::new_u(3, value).unwrap());
938                let get_r = builder
939                    .add_array_set(int_ty.clone(), 2, arr, index, value)
940                    .unwrap();
941                let r = {
942                    let res_sum_ty = {
943                        let row = vec![int_ty.clone(), array_type(2, int_ty.clone())];
944                        either_type(row.clone(), row)
945                    };
946                    let variants = (0..res_sum_ty.num_variants())
947                        .map(|i| {
948                            res_sum_ty
949                                .get_variant(i)
950                                .cloned()
951                                .unwrap()
952                                .try_into()
953                                .unwrap()
954                        })
955                        .collect_vec();
956                    let mut builder = builder
957                        .conditional_builder((variants, get_r), [], bool_t().into())
958                        .unwrap();
959                    for i in 0..2 {
960                        let mut builder = builder.case_builder(i).unwrap();
961                        let [elem, arr] = builder.input_wires_arr();
962                        let expected_elem =
963                            builder.add_load_value(ConstInt::new_u(3, expected_elem).unwrap());
964                        let expected_arr_0 =
965                            builder.add_load_value(ConstInt::new_u(3, expected_arr[0]).unwrap());
966                        let expected_arr_1 =
967                            builder.add_load_value(ConstInt::new_u(3, expected_arr[1]).unwrap());
968                        let (r, arr) = builder.add_array_get(int_ty.clone(), 2, arr, us0).unwrap();
969                        let [arr_0] = builder
970                            .build_unwrap_sum(1, option_type(int_ty.clone()), r)
971                            .unwrap();
972                        let (r, arr) = builder.add_array_get(int_ty.clone(), 2, arr, us1).unwrap();
973                        let [arr_1] = builder
974                            .build_unwrap_sum(1, option_type(int_ty.clone()), r)
975                            .unwrap();
976                        let elem_eq = builder.add_ieq(3, elem, expected_elem).unwrap();
977                        let arr_0_eq = builder.add_ieq(3, arr_0, expected_arr_0).unwrap();
978                        let arr_1_eq = builder.add_ieq(3, arr_1, expected_arr_1).unwrap();
979                        let r = builder.add_and(elem_eq, arr_0_eq).unwrap();
980                        let r = builder.add_and(r, arr_1_eq).unwrap();
981                        builder.add_array_discard(int_ty.clone(), 2, arr).unwrap();
982                        builder.finish_with_outputs([r]).unwrap();
983                    }
984                    builder.finish_sub_container().unwrap().out_wire(0)
985                };
986                let r = {
987                    let mut conditional = builder
988                        .conditional_builder(([type_row![], type_row![]], r), [], usize_t().into())
989                        .unwrap();
990                    conditional
991                        .case_builder(0)
992                        .unwrap()
993                        .finish_with_outputs([us0])
994                        .unwrap();
995                    conditional
996                        .case_builder(1)
997                        .unwrap()
998                        .finish_with_outputs([us1])
999                        .unwrap();
1000                    conditional.finish_sub_container().unwrap().out_wire(0)
1001                };
1002                builder.finish_hugr_with_outputs([r]).unwrap()
1003            });
1004        exec_ctx.add_extensions(|cge| {
1005            cge.add_default_prelude_extensions()
1006                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1007                .add_default_int_extensions()
1008                .add_logic_extensions()
1009        });
1010        assert_eq!(1, exec_ctx.exec_hugr_u64(hugr, "main"));
1011    }
1012
1013    #[rstest]
1014    #[case(0, 1, [2,1], true)]
1015    #[case(0, 0, [1,2], true)]
1016    #[case(0, 2, [1,2], false)]
1017    #[case(2, 0, [1,2], false)]
1018    #[case(9999999, 0, [1,2], false)]
1019    #[case(0, 9999999, [1,2], false)]
1020    fn exec_swap(
1021        mut exec_ctx: TestContext,
1022        #[case] index1: u64,
1023        #[case] index2: u64,
1024        #[case] expected_arr: [u64; 2],
1025        #[case] expected_succeeded: bool,
1026    ) {
1027        // We build a HUGR that:
1028        // - Creates an array: [1 ,2]
1029        // - Swaps the elements at the given indices
1030        // - Checks the following, returning 1 iff the following are all true:
1031        //  - The element at index 0 is `expected_elem_0`
1032        //  - The swap operation succeeded iff `expected_succeeded`
1033
1034        let int_ty = int_type(3);
1035        let arr_ty = array_type(2, int_ty.clone());
1036        let hugr = SimpleHugrConfig::new()
1037            .with_outs(usize_t())
1038            .with_extensions(exec_registry())
1039            .finish(|mut builder| {
1040                let us0 = builder.add_load_value(ConstUsize::new(0));
1041                let us1 = builder.add_load_value(ConstUsize::new(1));
1042                let i1 = builder.add_load_value(ConstInt::new_u(3, 1).unwrap());
1043                let i2 = builder.add_load_value(ConstInt::new_u(3, 2).unwrap());
1044                let arr = builder.add_new_array(int_ty.clone(), [i1, i2]).unwrap();
1045
1046                let index1 = builder.add_load_value(ConstUsize::new(index1));
1047                let index2 = builder.add_load_value(ConstUsize::new(index2));
1048                let r = builder
1049                    .add_array_swap(int_ty.clone(), 2, arr, index1, index2)
1050                    .unwrap();
1051                let [arr, was_expected_success] = {
1052                    let mut conditional = builder
1053                        .conditional_builder(
1054                            (
1055                                [vec![arr_ty.clone()].into(), vec![arr_ty.clone()].into()],
1056                                r,
1057                            ),
1058                            [],
1059                            vec![arr_ty, bool_t()].into(),
1060                        )
1061                        .unwrap();
1062                    for i in 0..2 {
1063                        let mut case = conditional.case_builder(i).unwrap();
1064                        let [arr] = case.input_wires_arr();
1065                        let was_expected_success =
1066                            case.add_load_value(if (i == 1) == expected_succeeded {
1067                                Value::true_val()
1068                            } else {
1069                                Value::false_val()
1070                            });
1071                        case.finish_with_outputs([arr, was_expected_success])
1072                            .unwrap();
1073                    }
1074                    conditional.finish_sub_container().unwrap().outputs_arr()
1075                };
1076                let (r, arr) = builder.add_array_get(int_ty.clone(), 2, arr, us0).unwrap();
1077                let elem_0 = builder
1078                    .build_unwrap_sum::<1>(1, option_type(int_ty.clone()), r)
1079                    .unwrap()[0];
1080                let (r, arr) = builder.add_array_get(int_ty.clone(), 2, arr, us1).unwrap();
1081                let elem_1 = builder
1082                    .build_unwrap_sum::<1>(1, option_type(int_ty.clone()), r)
1083                    .unwrap()[0];
1084                let expected_elem_0 =
1085                    builder.add_load_value(ConstInt::new_u(3, expected_arr[0]).unwrap());
1086                let elem_0_ok = builder.add_ieq(3, elem_0, expected_elem_0).unwrap();
1087                let expected_elem_1 =
1088                    builder.add_load_value(ConstInt::new_u(3, expected_arr[1]).unwrap());
1089                let elem_1_ok = builder.add_ieq(3, elem_1, expected_elem_1).unwrap();
1090                let r = builder.add_and(was_expected_success, elem_0_ok).unwrap();
1091                let r = builder.add_and(r, elem_1_ok).unwrap();
1092                let r = {
1093                    let mut conditional = builder
1094                        .conditional_builder(([type_row![], type_row![]], r), [], usize_t().into())
1095                        .unwrap();
1096                    conditional
1097                        .case_builder(0)
1098                        .unwrap()
1099                        .finish_with_outputs([us0])
1100                        .unwrap();
1101                    conditional
1102                        .case_builder(1)
1103                        .unwrap()
1104                        .finish_with_outputs([us1])
1105                        .unwrap();
1106                    conditional.finish_sub_container().unwrap().out_wire(0)
1107                };
1108                builder.add_array_discard(int_ty.clone(), 2, arr).unwrap();
1109                builder.finish_hugr_with_outputs([r]).unwrap()
1110            });
1111        exec_ctx.add_extensions(|cge| {
1112            cge.add_default_prelude_extensions()
1113                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1114                .add_default_int_extensions()
1115                .add_logic_extensions()
1116        });
1117        assert_eq!(1, exec_ctx.exec_hugr_u64(hugr, "main"));
1118    }
1119
1120    #[rstest]
1121    #[case(0, 5)]
1122    #[case(1, 5)]
1123    fn exec_clone(mut exec_ctx: TestContext, #[case] index: u64, #[case] new_v: u64) {
1124        // We build a HUGR that:
1125        // - Creates an array: [1, 2]
1126        // - Clones the array
1127        // - Mutates the original at the given index
1128        // - Returns the unchanged element of the cloned array
1129
1130        let int_ty = int_type(3);
1131        let arr_ty = array_type(2, int_ty.clone());
1132        let hugr = SimpleHugrConfig::new()
1133            .with_outs(int_ty.clone())
1134            .with_extensions(exec_registry())
1135            .finish(|mut builder| {
1136                let idx = builder.add_load_value(ConstUsize::new(index));
1137                let i1 = builder.add_load_value(ConstInt::new_u(3, 1).unwrap());
1138                let i2 = builder.add_load_value(ConstInt::new_u(3, 2).unwrap());
1139                let inew = builder.add_load_value(ConstInt::new_u(3, new_v).unwrap());
1140                let arr = builder.add_new_array(int_ty.clone(), [i1, i2]).unwrap();
1141
1142                let (arr, arr_clone) = builder.add_array_clone(int_ty.clone(), 2, arr).unwrap();
1143                let r = builder
1144                    .add_array_set(int_ty.clone(), 2, arr, idx, inew)
1145                    .unwrap();
1146                let [_, arr] = builder
1147                    .build_unwrap_sum(
1148                        1,
1149                        either_type(
1150                            vec![int_ty.clone(), arr_ty.clone()],
1151                            vec![int_ty.clone(), arr_ty.clone()],
1152                        ),
1153                        r,
1154                    )
1155                    .unwrap();
1156                let (r, arr_clone) = builder
1157                    .add_array_get(int_ty.clone(), 2, arr_clone, idx)
1158                    .unwrap();
1159                let [elem] = builder
1160                    .build_unwrap_sum(1, option_type(int_ty.clone()), r)
1161                    .unwrap();
1162                builder.add_array_discard(int_ty.clone(), 2, arr).unwrap();
1163                builder
1164                    .add_array_discard(int_ty.clone(), 2, arr_clone)
1165                    .unwrap();
1166                builder.finish_hugr_with_outputs([elem]).unwrap()
1167            });
1168        exec_ctx.add_extensions(|cge| {
1169            cge.add_default_prelude_extensions()
1170                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1171                .add_default_int_extensions()
1172                .add_logic_extensions()
1173        });
1174        assert_eq!([1, 2][index as usize], exec_ctx.exec_hugr_u64(hugr, "main"));
1175    }
1176
1177    #[rstest]
1178    #[case(&[], 0)]
1179    #[case(&[true], 1)]
1180    #[case(&[false], 4)]
1181    #[case(&[true, true], 3)]
1182    #[case(&[false, false], 6)]
1183    #[case(&[true, false, true], 7)]
1184    #[case(&[false, true, false], 7)]
1185    fn exec_pop(mut exec_ctx: TestContext, #[case] from_left: &[bool], #[case] expected: u64) {
1186        // We build a HUGR that:
1187        // - Creates an array: [1,2,4]
1188        // - Pops `num` elements from the left or right
1189        // - Returns the sum of the popped elements
1190
1191        let array_contents = [1, 2, 4];
1192        let int_ty = int_type(6);
1193        let hugr = SimpleHugrConfig::new()
1194            .with_outs(int_ty.clone())
1195            .with_extensions(exec_registry())
1196            .finish(|mut builder| {
1197                let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
1198                let new_array_args = array_contents
1199                    .iter()
1200                    .map(|&i| builder.add_load_value(ConstInt::new_u(6, i).unwrap()))
1201                    .collect_vec();
1202                let mut arr = builder
1203                    .add_new_array(int_ty.clone(), new_array_args)
1204                    .unwrap();
1205                for (i, left) in from_left.iter().enumerate() {
1206                    let array_size = (array_contents.len() - i) as u64;
1207                    let pop_res = if *left {
1208                        builder
1209                            .add_array_pop_left(int_ty.clone(), array_size, arr)
1210                            .unwrap()
1211                    } else {
1212                        builder
1213                            .add_array_pop_right(int_ty.clone(), array_size, arr)
1214                            .unwrap()
1215                    };
1216                    let [elem, new_arr] = builder
1217                        .build_unwrap_sum(
1218                            1,
1219                            option_type(vec![
1220                                int_ty.clone(),
1221                                array_type(array_size - 1, int_ty.clone()),
1222                            ]),
1223                            pop_res,
1224                        )
1225                        .unwrap();
1226                    arr = new_arr;
1227                    r = builder.add_iadd(6, r, elem).unwrap();
1228                }
1229                builder
1230                    .add_array_discard(
1231                        int_ty.clone(),
1232                        (array_contents.len() - from_left.len()) as u64,
1233                        arr,
1234                    )
1235                    .unwrap();
1236                builder.finish_hugr_with_outputs([r]).unwrap()
1237            });
1238        exec_ctx.add_extensions(|cge| {
1239            cge.add_default_prelude_extensions()
1240                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1241                .add_default_int_extensions()
1242        });
1243        assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
1244    }
1245
1246    #[rstest]
1247    #[case(&[], 0)]
1248    #[case(&[1, 2], 3)]
1249    #[case(&[6, 6, 6], 18)]
1250    fn exec_unpack(
1251        mut exec_ctx: TestContext,
1252        #[case] array_contents: &[u64],
1253        #[case] expected: u64,
1254    ) {
1255        // We build a HUGR that:
1256        // - Loads an array with the given contents
1257        // - Unpacks all the elements
1258        // - Returns the sum of the elements
1259
1260        let int_ty = int_type(6);
1261        let hugr = SimpleHugrConfig::new()
1262            .with_outs(int_ty.clone())
1263            .with_extensions(exec_registry())
1264            .finish(|mut builder| {
1265                let array = array::ArrayValue::new(
1266                    int_ty.clone(),
1267                    array_contents
1268                        .iter()
1269                        .map(|&i| ConstInt::new_u(6, i).unwrap().into())
1270                        .collect_vec(),
1271                );
1272                let array = builder.add_load_value(array);
1273                let unpacked = builder
1274                    .add_array_unpack(int_ty.clone(), array_contents.len() as u64, array)
1275                    .unwrap();
1276                let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
1277                for elem in unpacked {
1278                    r = builder.add_iadd(6, r, elem).unwrap();
1279                }
1280
1281                builder.finish_hugr_with_outputs([r]).unwrap()
1282            });
1283        exec_ctx.add_extensions(|cge| {
1284            cge.add_default_prelude_extensions()
1285                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1286                .add_default_int_extensions()
1287        });
1288        assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
1289    }
1290
1291    #[rstest]
1292    #[case(5, 42, 0)]
1293    #[case(5, 42, 1)]
1294    #[case(5, 42, 2)]
1295    #[case(5, 42, 3)]
1296    #[case(5, 42, 4)]
1297    fn exec_repeat(
1298        mut exec_ctx: TestContext,
1299        #[case] size: u64,
1300        #[case] value: u64,
1301        #[case] idx: u64,
1302    ) {
1303        // We build a HUGR that:
1304        // - Contains a nested function that returns `value`
1305        // - Creates an array of length `size` populated via this function
1306        // - Looks up the value at `idx` and returns it
1307
1308        let int_ty = int_type(6);
1309        let hugr = SimpleHugrConfig::new()
1310            .with_outs(int_ty.clone())
1311            .with_extensions(exec_registry())
1312            .finish(|mut builder| {
1313                let mut mb = builder.module_root_builder();
1314                let mut func = mb
1315                    .define_function("foo", Signature::new(vec![], vec![int_ty.clone()]))
1316                    .unwrap();
1317                let v = func.add_load_value(ConstInt::new_u(6, value).unwrap());
1318                let func_id = func.finish_with_outputs(vec![v]).unwrap();
1319                let func_v = builder.load_func(func_id.handle(), &[]).unwrap();
1320                let repeat = ArrayRepeat::new(int_ty.clone(), size);
1321                let arr = builder
1322                    .add_dataflow_op(repeat, vec![func_v])
1323                    .unwrap()
1324                    .out_wire(0);
1325                let idx_v = builder.add_load_value(ConstUsize::new(idx));
1326                let (get_res, arr) = builder
1327                    .add_array_get(int_ty.clone(), size, arr, idx_v)
1328                    .unwrap();
1329                let [elem] = builder
1330                    .build_unwrap_sum(1, option_type(vec![int_ty.clone()]), get_res)
1331                    .unwrap();
1332                builder
1333                    .add_array_discard(int_ty.clone(), size, arr)
1334                    .unwrap();
1335                builder.finish_hugr_with_outputs([elem]).unwrap()
1336            });
1337        exec_ctx.add_extensions(|cge| {
1338            cge.add_default_prelude_extensions()
1339                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1340                .add_default_int_extensions()
1341        });
1342        assert_eq!(value, exec_ctx.exec_hugr_u64(hugr, "main"));
1343    }
1344
1345    #[rstest]
1346    #[case(10, 1)]
1347    #[case(10, 2)]
1348    #[case(0, 1)]
1349    fn exec_scan_map(mut exec_ctx: TestContext, #[case] size: u64, #[case] inc: u64) {
1350        // We build a HUGR that:
1351        // - Creates an array [1, 2, 3, ..., size]
1352        // - Maps a function that increments each element by `inc`
1353        // - Returns the sum of the array elements
1354
1355        let int_ty = int_type(6);
1356        let hugr = SimpleHugrConfig::new()
1357            .with_outs(int_ty.clone())
1358            .with_extensions(exec_registry())
1359            .finish(|mut builder| {
1360                let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
1361                let new_array_args = (0..size)
1362                    .map(|i| builder.add_load_value(ConstInt::new_u(6, i).unwrap()))
1363                    .collect_vec();
1364                let arr = builder
1365                    .add_new_array(int_ty.clone(), new_array_args)
1366                    .unwrap();
1367
1368                let mut mb = builder.module_root_builder();
1369                let mut func = mb
1370                    .define_function(
1371                        "foo",
1372                        Signature::new(vec![int_ty.clone()], vec![int_ty.clone()]),
1373                    )
1374                    .unwrap();
1375                let [elem] = func.input_wires_arr();
1376                let delta = func.add_load_value(ConstInt::new_u(6, inc).unwrap());
1377                let out = func.add_iadd(6, elem, delta).unwrap();
1378                let func_id = func.finish_with_outputs(vec![out]).unwrap();
1379                let func_v = builder.load_func(func_id.handle(), &[]).unwrap();
1380                let scan = ArrayScan::new(int_ty.clone(), int_ty.clone(), vec![], size);
1381                let mut arr = builder
1382                    .add_dataflow_op(scan, [arr, func_v])
1383                    .unwrap()
1384                    .out_wire(0);
1385
1386                for i in 0..size {
1387                    let array_size = size - i;
1388                    let pop_res = builder
1389                        .add_array_pop_left(int_ty.clone(), array_size, arr)
1390                        .unwrap();
1391                    let [elem, new_arr] = builder
1392                        .build_unwrap_sum(
1393                            1,
1394                            option_type(vec![
1395                                int_ty.clone(),
1396                                array_type(array_size - 1, int_ty.clone()),
1397                            ]),
1398                            pop_res,
1399                        )
1400                        .unwrap();
1401                    arr = new_arr;
1402                    r = builder.add_iadd(6, r, elem).unwrap();
1403                }
1404                builder
1405                    .add_array_discard_empty(int_ty.clone(), arr)
1406                    .unwrap();
1407                builder.finish_hugr_with_outputs([r]).unwrap()
1408            });
1409        exec_ctx.add_extensions(|cge| {
1410            cge.add_default_prelude_extensions()
1411                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1412                .add_default_int_extensions()
1413        });
1414        let expected: u64 = (inc..size + inc).sum();
1415        assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
1416    }
1417
1418    #[rstest]
1419    #[case(0)]
1420    #[case(1)]
1421    #[case(10)]
1422    fn exec_scan_fold(mut exec_ctx: TestContext, #[case] size: u64) {
1423        // We build a HUGR that:
1424        // - Creates an array [1, 2, 3, ..., size]
1425        // - Sums up the elements of the array using a scan and returns that sum
1426        let int_ty = int_type(6);
1427        let hugr = SimpleHugrConfig::new()
1428            .with_outs(int_ty.clone())
1429            .with_extensions(exec_registry())
1430            .finish(|mut builder| {
1431                let new_array_args = (0..size)
1432                    .map(|i| builder.add_load_value(ConstInt::new_u(6, i).unwrap()))
1433                    .collect_vec();
1434                let arr = builder
1435                    .add_new_array(int_ty.clone(), new_array_args)
1436                    .unwrap();
1437
1438                let mut mb = builder.module_root_builder();
1439                let mut func = mb
1440                    .define_function(
1441                        "foo",
1442                        Signature::new(
1443                            vec![int_ty.clone(), int_ty.clone()],
1444                            vec![Type::UNIT, int_ty.clone()],
1445                        ),
1446                    )
1447                    .unwrap();
1448                let [elem, acc] = func.input_wires_arr();
1449                let acc = func.add_iadd(6, elem, acc).unwrap();
1450                let unit = func
1451                    .add_dataflow_op(Tag::new(0, vec![type_row![]]), [])
1452                    .unwrap()
1453                    .out_wire(0);
1454                let func_id = func.finish_with_outputs(vec![unit, acc]).unwrap();
1455                let func_v = builder.load_func(func_id.handle(), &[]).unwrap();
1456                let scan = ArrayScan::new(int_ty.clone(), Type::UNIT, vec![int_ty.clone()], size);
1457                let zero = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
1458                let [arr, sum] = builder
1459                    .add_dataflow_op(scan, [arr, func_v, zero])
1460                    .unwrap()
1461                    .outputs_arr();
1462                builder.add_array_discard(Type::UNIT, size, arr).unwrap();
1463                builder.finish_hugr_with_outputs([sum]).unwrap()
1464            });
1465        exec_ctx.add_extensions(|cge| {
1466            cge.add_default_prelude_extensions()
1467                .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen))
1468                .add_default_int_extensions()
1469        });
1470        let expected: u64 = (0..size).sum();
1471        assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
1472    }
1473}