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