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(note = "Use `hugr_llvm::extension::collections::array` instead")]
37pub trait ArrayCodegen: Clone {
38 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 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 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 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 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#[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
189fn 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
211fn 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
226fn 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 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 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 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 let builder = ctx.builder();
511 let array_v = with_array_alloca(builder, array_v, |ptr| {
512 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
599fn 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
643fn 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
672fn 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 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 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 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 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 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 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 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 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 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}