1#![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#[deprecated(
37 note = "Use `hugr_llvm::extension::collections::array` instead",
38 since = "0.20.0"
39)]
40pub trait ArrayCodegen: Clone {
41 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 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 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 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 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#[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
198fn 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
220fn 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
235fn 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 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 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 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 let builder = ctx.builder();
520 let array_v = with_array_alloca(builder, array_v, |ptr| {
521 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
608fn 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
652fn 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
681fn 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 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 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 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 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 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 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 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 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 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}