1use std::iter;
22use std::sync::LazyLock;
23
24use anyhow::{Ok, Result, anyhow};
25use hugr_core::extension::prelude::{ConstError, option_type, usize_t};
26use hugr_core::extension::simple_op::{MakeExtensionOp, MakeOpDef, MakeRegisteredOp};
27use hugr_core::ops::DataflowOpTrait;
28use hugr_core::std_extensions::collections::array;
29use hugr_core::std_extensions::collections::borrow_array::{
30 self, BArrayClone, BArrayDiscard, BArrayFromArray, BArrayFromArrayDef, BArrayOp, BArrayOpDef,
31 BArrayRepeat, BArrayScan, BArrayToArray, BArrayToArrayDef, BArrayUnsafeOp, BArrayUnsafeOpDef,
32 borrow_array_type,
33};
34use hugr_core::types::{TypeArg, TypeEnum};
35use hugr_core::{HugrView, Node};
36use inkwell::builder::Builder;
37use inkwell::intrinsics::Intrinsic;
38use inkwell::types::{BasicType, BasicTypeEnum, IntType, StructType};
39use inkwell::values::{
40 BasicValue as _, BasicValueEnum, CallableValue, IntValue, PointerValue, StructValue,
41};
42use inkwell::{AddressSpace, IntPredicate};
43use itertools::Itertools;
44
45use crate::emit::emit_value;
46use crate::emit::func::get_or_make_function;
47use crate::emit::libc::{emit_libc_free, emit_libc_malloc};
48use crate::extension::PreludeCodegen;
49use crate::extension::collections::array::{build_array_fat_pointer, decompose_array_fat_pointer};
50use crate::{CodegenExtension, CodegenExtsBuilder};
51use crate::{
52 emit::{EmitFuncContext, RowPromise, deaggregate_call_result},
53 types::{HugrType, TypingSession},
54};
55
56static ERR_ALREADY_BORROWED: LazyLock<ConstError> = LazyLock::new(|| ConstError {
57 signal: 2,
58 message: "Array element is already borrowed".to_string(),
59});
60
61static ERR_NOT_BORROWED: LazyLock<ConstError> = LazyLock::new(|| ConstError {
62 signal: 2,
63 message: "Array already contains an element at this index".to_string(),
64});
65
66static ERR_OUT_OF_BOUNDS: LazyLock<ConstError> = LazyLock::new(|| ConstError {
67 signal: 2,
68 message: "Index out of bounds".to_string(),
69});
70
71static ERR_NOT_ALL_BORROWED: LazyLock<ConstError> = LazyLock::new(|| ConstError {
72 signal: 2,
73 message: "Array contains non-borrowed elements and cannot be discarded".to_string(),
74});
75
76static ERR_SOME_BORROWED: LazyLock<ConstError> = LazyLock::new(|| ConstError {
77 signal: 2,
78 message: "Some array elements have been borrowed".to_string(),
79});
80
81impl<'a, H: HugrView<Node = Node> + 'a> CodegenExtsBuilder<'a, H> {
82 #[must_use]
85 pub fn add_default_borrow_array_extensions(self, pcg: impl PreludeCodegen + 'a) -> Self {
86 self.add_borrow_array_extensions(DefaultBorrowArrayCodegen(pcg))
87 }
88
89 pub fn add_borrow_array_extensions(self, ccg: impl BorrowArrayCodegen + 'a) -> Self {
92 self.add_extension(BorrowArrayCodegenExtension::from(ccg))
93 }
94}
95
96pub trait BorrowArrayCodegen: Clone {
106 fn emit_panic<H: HugrView<Node = Node>>(
110 &self,
111 ctx: &mut EmitFuncContext<H>,
112 err: BasicValueEnum,
113 ) -> Result<()>;
114
115 fn emit_allocate_array<'c, H: HugrView<Node = Node>>(
120 &self,
121 ctx: &mut EmitFuncContext<'c, '_, H>,
122 size: IntValue<'c>,
123 ) -> Result<PointerValue<'c>> {
124 let ptr = emit_libc_malloc(ctx, size.into())?;
125 Ok(ptr.into_pointer_value())
126 }
127
128 fn emit_free_array<'c, H: HugrView<Node = Node>>(
132 &self,
133 ctx: &mut EmitFuncContext<'c, '_, H>,
134 ptr: PointerValue<'c>,
135 ) -> Result<()> {
136 emit_libc_free(ctx, ptr.into())
137 }
138
139 fn array_type<'c>(
141 &self,
142 session: &TypingSession<'c, '_>,
143 elem_ty: BasicTypeEnum<'c>,
144 _size: u64,
145 ) -> impl BasicType<'c> {
146 barray_fat_pointer_ty(session, elem_ty)
147 }
148
149 fn emit_array_value<'c, H: HugrView<Node = Node>>(
151 &self,
152 ctx: &mut EmitFuncContext<'c, '_, H>,
153 value: &borrow_array::BArrayValue,
154 ) -> Result<BasicValueEnum<'c>> {
155 emit_barray_value(self, ctx, value)
156 }
157
158 fn emit_array_op<'c, H: HugrView<Node = Node>>(
160 &self,
161 ctx: &mut EmitFuncContext<'c, '_, H>,
162 op: BArrayOp,
163 inputs: Vec<BasicValueEnum<'c>>,
164 outputs: RowPromise<'c>,
165 ) -> Result<()> {
166 emit_barray_op(self, ctx, op, inputs, outputs)
167 }
168
169 fn emit_array_unsafe_op<'c, H: HugrView<Node = Node>>(
171 &self,
172 ctx: &mut EmitFuncContext<'c, '_, H>,
173 op: BArrayUnsafeOp,
174 inputs: Vec<BasicValueEnum<'c>>,
175 outputs: RowPromise<'c>,
176 ) -> Result<()> {
177 emit_barray_unsafe_op(self, ctx, op, inputs, outputs)
178 }
179
180 fn emit_array_clone<'c, H: HugrView<Node = Node>>(
182 &self,
183 ctx: &mut EmitFuncContext<'c, '_, H>,
184 op: BArrayClone,
185 array_v: BasicValueEnum<'c>,
186 ) -> Result<(BasicValueEnum<'c>, BasicValueEnum<'c>)> {
187 emit_clone_op(self, ctx, op, array_v)
188 }
189
190 fn emit_array_discard<'c, H: HugrView<Node = Node>>(
192 &self,
193 ctx: &mut EmitFuncContext<'c, '_, H>,
194 op: BArrayDiscard,
195 array_v: BasicValueEnum<'c>,
196 ) -> Result<()> {
197 emit_barray_discard(self, ctx, op, array_v)
198 }
199
200 fn emit_array_repeat<'c, H: HugrView<Node = Node>>(
202 &self,
203 ctx: &mut EmitFuncContext<'c, '_, H>,
204 op: BArrayRepeat,
205 func: BasicValueEnum<'c>,
206 ) -> Result<BasicValueEnum<'c>> {
207 emit_repeat_op(self, ctx, op, func)
208 }
209
210 fn emit_array_scan<'c, H: HugrView<Node = Node>>(
214 &self,
215 ctx: &mut EmitFuncContext<'c, '_, H>,
216 op: BArrayScan,
217 src_array: BasicValueEnum<'c>,
218 func: BasicValueEnum<'c>,
219 initial_accs: &[BasicValueEnum<'c>],
220 ) -> Result<(BasicValueEnum<'c>, Vec<BasicValueEnum<'c>>)> {
221 emit_scan_op(
222 self,
223 ctx,
224 op,
225 src_array.into_struct_value(),
226 func,
227 initial_accs,
228 )
229 }
230
231 fn emit_to_array_op<'c, H: HugrView<Node = Node>>(
233 &self,
234 ctx: &mut EmitFuncContext<'c, '_, H>,
235 op: BArrayToArray,
236 barray_v: BasicValueEnum<'c>,
237 ) -> Result<BasicValueEnum<'c>> {
238 emit_to_array_op(self, ctx, op, barray_v)
239 }
240
241 fn emit_from_array_op<'c, H: HugrView<Node = Node>>(
243 &self,
244 ctx: &mut EmitFuncContext<'c, '_, H>,
245 op: BArrayFromArray,
246 array_v: BasicValueEnum<'c>,
247 ) -> Result<BasicValueEnum<'c>> {
248 emit_from_array_op(self, ctx, op, array_v)
249 }
250}
251
252#[derive(Default, Clone)]
255pub struct DefaultBorrowArrayCodegen<PCG>(PCG);
256
257impl<PCG: PreludeCodegen> BorrowArrayCodegen for DefaultBorrowArrayCodegen<PCG> {
258 fn emit_panic<H: HugrView<Node = Node>>(
259 &self,
260 ctx: &mut EmitFuncContext<H>,
261 err: BasicValueEnum,
262 ) -> Result<()> {
263 self.0.emit_panic(ctx, err)
264 }
265}
266
267#[derive(Clone, Debug, Default)]
268pub struct BorrowArrayCodegenExtension<CCG>(CCG);
269
270impl<CCG: BorrowArrayCodegen> BorrowArrayCodegenExtension<CCG> {
271 pub fn new(ccg: CCG) -> Self {
272 Self(ccg)
273 }
274}
275
276impl<CCG: BorrowArrayCodegen> From<CCG> for BorrowArrayCodegenExtension<CCG> {
277 fn from(ccg: CCG) -> Self {
278 Self::new(ccg)
279 }
280}
281
282impl<CCG: BorrowArrayCodegen> CodegenExtension for BorrowArrayCodegenExtension<CCG> {
283 fn add_extension<'a, H: HugrView<Node = Node> + 'a>(
284 self,
285 builder: CodegenExtsBuilder<'a, H>,
286 ) -> CodegenExtsBuilder<'a, H>
287 where
288 Self: 'a,
289 {
290 builder
291 .custom_type(
292 (
293 borrow_array::EXTENSION_ID,
294 borrow_array::BORROW_ARRAY_TYPENAME,
295 ),
296 {
297 let ccg = self.0.clone();
298 move |ts, hugr_type| {
299 let [TypeArg::BoundedNat(n), TypeArg::Runtime(ty)] = hugr_type.args()
300 else {
301 return Err(anyhow!("Invalid type args for array type"));
302 };
303 let elem_ty = ts.llvm_type(ty)?;
304 Ok(ccg.array_type(&ts, elem_ty, *n).as_basic_type_enum())
305 }
306 },
307 )
308 .custom_const::<borrow_array::BArrayValue>({
309 let ccg = self.0.clone();
310 move |context, k| ccg.emit_array_value(context, k)
311 })
312 .simple_extension_op::<BArrayOpDef>({
313 let ccg = self.0.clone();
314 move |context, args, _| {
315 ccg.emit_array_op(
316 context,
317 BArrayOp::from_extension_op(args.node().as_ref())?,
318 args.inputs,
319 args.outputs,
320 )
321 }
322 })
323 .simple_extension_op::<BArrayUnsafeOpDef>({
324 let ccg = self.0.clone();
325 move |context, args, _| {
326 ccg.emit_array_unsafe_op(
327 context,
328 BArrayUnsafeOp::from_extension_op(args.node().as_ref())?,
329 args.inputs,
330 args.outputs,
331 )
332 }
333 })
334 .extension_op(borrow_array::EXTENSION_ID, array::ARRAY_CLONE_OP_ID, {
335 let ccg = self.0.clone();
336 move |context, args| {
337 let arr = args.inputs[0];
338 let op = BArrayClone::from_extension_op(args.node().as_ref())?;
339 let (arr1, arr2) = ccg.emit_array_clone(context, op, arr)?;
340 args.outputs.finish(context.builder(), [arr1, arr2])
341 }
342 })
343 .extension_op(borrow_array::EXTENSION_ID, array::ARRAY_DISCARD_OP_ID, {
344 let ccg = self.0.clone();
345 move |context, args| {
346 let arr = args.inputs[0];
347 let op = BArrayDiscard::from_extension_op(args.node().as_ref())?;
348 ccg.emit_array_discard(context, op, arr)?;
349 args.outputs.finish(context.builder(), [])
350 }
351 })
352 .extension_op(borrow_array::EXTENSION_ID, array::ARRAY_REPEAT_OP_ID, {
353 let ccg = self.0.clone();
354 move |context, args| {
355 let func = args.inputs[0];
356 let op = BArrayRepeat::from_extension_op(args.node().as_ref())?;
357 let arr = ccg.emit_array_repeat(context, op, func)?;
358 args.outputs.finish(context.builder(), [arr])
359 }
360 })
361 .extension_op(borrow_array::EXTENSION_ID, array::ARRAY_SCAN_OP_ID, {
362 let ccg = self.0.clone();
363 move |context, args| {
364 let src_array = args.inputs[0];
365 let func = args.inputs[1];
366 let initial_accs = &args.inputs[2..];
367 let op = BArrayScan::from_extension_op(args.node().as_ref())?;
368 let (tgt_array, final_accs) =
369 ccg.emit_array_scan(context, op, src_array, func, initial_accs)?;
370 args.outputs
371 .finish(context.builder(), iter::once(tgt_array).chain(final_accs))
372 }
373 })
374 .extension_op(
375 borrow_array::EXTENSION_ID,
376 BArrayToArrayDef::new().opdef_id(),
377 {
378 let ccg = self.0.clone();
379 move |context, args| {
380 let barray = args.inputs[0];
381 let op = BArrayToArray::from_extension_op(args.node().as_ref())?;
382 let array = ccg.emit_to_array_op(context, op, barray)?;
383 args.outputs.finish(context.builder(), [array])
384 }
385 },
386 )
387 .extension_op(
388 borrow_array::EXTENSION_ID,
389 BArrayFromArrayDef::new().opdef_id(),
390 {
391 let ccg = self.0.clone();
392 move |context, args| {
393 let array = args.inputs[0];
394 let op = BArrayFromArray::from_extension_op(args.node().as_ref())?;
395 let barray = ccg.emit_from_array_op(context, op, array)?;
396 args.outputs.finish(context.builder(), [barray])
397 }
398 },
399 )
400 }
401}
402
403fn usize_ty<'c>(ts: &TypingSession<'c, '_>) -> IntType<'c> {
404 ts.llvm_type(&usize_t())
405 .expect("Prelude codegen is registered")
406 .into_int_type()
407}
408
409#[must_use]
411pub fn barray_fat_pointer_ty<'c>(
412 session: &TypingSession<'c, '_>,
413 elem_ty: BasicTypeEnum<'c>,
414) -> StructType<'c> {
415 let iw_ctx = session.iw_context();
416 let usize_t = usize_ty(session);
417 iw_ctx.struct_type(
418 &[
419 elem_ty.ptr_type(AddressSpace::default()).into(),
421 usize_t.ptr_type(AddressSpace::default()).into(),
423 usize_t.into(),
425 ],
426 false,
427 )
428}
429
430pub fn build_barray_fat_pointer<'c, H: HugrView<Node = Node>>(
432 ctx: &mut EmitFuncContext<'c, '_, H>,
433 BArrayFatPtrComponents {
434 elems_ptr,
435 mask_ptr,
436 offset,
437 }: BArrayFatPtrComponents<'c>,
438) -> Result<StructValue<'c>> {
439 let array_ty = barray_fat_pointer_ty(
440 &ctx.typing_session(),
441 elems_ptr.get_type().get_element_type().try_into().unwrap(),
442 );
443 let array_v = array_ty.get_poison();
444 let array_v =
445 ctx.builder()
446 .build_insert_value(array_v, elems_ptr.as_basic_value_enum(), 0, "")?;
447 let array_v =
448 ctx.builder()
449 .build_insert_value(array_v, mask_ptr.as_basic_value_enum(), 1, "")?;
450 let array_v = ctx
451 .builder()
452 .build_insert_value(array_v, offset.as_basic_value_enum(), 2, "")?;
453 Ok(array_v.into_struct_value())
454}
455
456pub struct BArrayFatPtrComponents<'a> {
457 pub elems_ptr: PointerValue<'a>,
458 pub mask_ptr: PointerValue<'a>,
459 pub offset: IntValue<'a>,
460}
461
462pub fn decompose_barray_fat_pointer<'c>(
464 builder: &Builder<'c>,
465 array_v: BasicValueEnum<'c>,
466) -> Result<BArrayFatPtrComponents<'c>> {
467 let array_v = array_v.into_struct_value();
468 let elems_ptr = builder
469 .build_extract_value(array_v, 0, "array_ptr")?
470 .into_pointer_value();
471 let mask_ptr = builder
472 .build_extract_value(array_v, 1, "array_mask_ptr")?
473 .into_pointer_value();
474 let offset = builder
475 .build_extract_value(array_v, 2, "array_offset")?
476 .into_int_value();
477 Ok(BArrayFatPtrComponents {
478 elems_ptr,
479 mask_ptr,
480 offset,
481 })
482}
483
484fn alloc_typed_array<'c, H: HugrView<Node = Node>>(
487 ccg: &impl BorrowArrayCodegen,
488 ctx: &mut EmitFuncContext<'c, '_, H>,
489 elem_ty: BasicTypeEnum<'c>,
490 num_elems: IntValue<'c>,
491) -> Result<(PointerValue<'c>, IntValue<'c>)> {
492 let size = ctx
493 .builder()
494 .build_int_mul(num_elems, elem_ty.size_of().unwrap(), "array_size")?;
495 let ptr = ccg.emit_allocate_array(ctx, size)?;
496 Ok((
497 ctx.builder()
498 .build_bit_cast(ptr, elem_ty.ptr_type(AddressSpace::default()), "")?
499 .into_pointer_value(),
500 size,
501 ))
502}
503
504pub fn build_barray_alloc<'c, H: HugrView<Node = Node>>(
510 ctx: &mut EmitFuncContext<'c, '_, H>,
511 ccg: &impl BorrowArrayCodegen,
512 elem_ty: BasicTypeEnum<'c>,
513 size: u64,
514 set_borrowed: bool,
515) -> Result<(PointerValue<'c>, StructValue<'c>)> {
516 let usize_t = usize_ty(&ctx.typing_session());
517 let length = usize_t.const_int(size, false);
518 let (elems_ptr, _) = alloc_typed_array(ccg, ctx, elem_ty, length)?;
519
520 let mask_length = usize_t.const_int(size.div_ceil(usize_t.get_bit_width() as u64), false);
522 let (mask_ptr, mask_size_value) = alloc_typed_array(ccg, ctx, usize_t.into(), mask_length)?;
523 fill_mask(ctx, mask_ptr, mask_size_value, set_borrowed)?;
524
525 let offset = usize_t.const_zero();
526 let array_v = build_barray_fat_pointer(
527 ctx,
528 BArrayFatPtrComponents {
529 elems_ptr,
530 mask_ptr,
531 offset,
532 },
533 )?;
534 Ok((elems_ptr, array_v))
535}
536
537fn fill_mask<H: HugrView<Node = Node>>(
539 ctx: &mut EmitFuncContext<H>,
540 mask_ptr: PointerValue,
541 size: IntValue,
542 value: bool,
543) -> Result<()> {
544 let memset = Intrinsic::find("llvm.memset")
545 .unwrap()
546 .get_declaration(
547 ctx.get_current_module(),
548 &[mask_ptr.get_type().into(), size.get_type().into()],
549 )
550 .unwrap();
551 let i8t = ctx.iw_context().i8_type(); let val = if value {
553 i8t.const_all_ones()
554 } else {
555 i8t.const_zero()
556 };
557 let volatile = ctx.iw_context().bool_type().const_zero().into();
558 ctx.builder().build_call(
559 memset,
560 &[mask_ptr.into(), val.into(), size.into(), volatile],
561 "",
562 )?;
563 Ok(())
564}
565
566#[derive(Clone, Copy, Debug, PartialEq, Eq)]
568enum MaskCheck {
569 Return,
571 CheckNotBorrowed,
573 Borrow,
575}
576
577impl MaskCheck {
578 fn func_name(&self) -> &'static str {
579 match self {
580 MaskCheck::Return => "__barray_mask_return",
581 MaskCheck::CheckNotBorrowed => "__barray_mask_check_not_borrowed",
582 MaskCheck::Borrow => "__barray_mask_borrow",
583 }
584 }
585
586 fn emit<'c, H: HugrView<Node = Node>>(
589 &self,
590 ccg: &impl BorrowArrayCodegen,
591 ctx: &mut EmitFuncContext<'c, '_, H>,
592 mask_ptr: PointerValue<'c>,
593 idx: IntValue<'c>,
594 ) -> Result<()> {
595 get_or_make_function(
596 ctx,
597 self.func_name(),
598 [mask_ptr.into(), idx.into()],
599 None,
600 |ctx, [mask_ptr, idx]| {
601 let usize_t = usize_ty(&ctx.typing_session());
603 let (
604 BlockData {
605 block_ptr,
606 block,
607 idx_in_block,
608 },
609 bit,
610 ) = inspect_mask_idx_bit(ctx, mask_ptr, idx)?;
611
612 let panic_bb = ctx.build_positioned_new_block("panic", None, |ctx, panic_bb| {
613 let err: &ConstError = match self {
614 MaskCheck::CheckNotBorrowed | MaskCheck::Borrow => &ERR_ALREADY_BORROWED,
615 MaskCheck::Return => &ERR_NOT_BORROWED,
616 };
617 let err_val = ctx.emit_custom_const(err).unwrap();
618 ccg.emit_panic(ctx, err_val)?;
619 ctx.builder().build_unreachable()?;
620 Ok(panic_bb)
621 })?;
622 let ok_bb = ctx.build_positioned_new_block("ok", None, |ctx, ok_bb| {
623 if let MaskCheck::Return | MaskCheck::Borrow = self {
624 let builder = ctx.builder();
626 let update = builder.build_left_shift(
627 usize_t.const_int(1, false),
628 idx_in_block,
629 "",
630 )?;
631 let block = builder.build_xor(block, update, "")?;
632 builder.build_store(block_ptr, block)?;
633 }
634 ctx.builder().build_return(None)?;
635 Ok(ok_bb)
636 })?;
637 let (if_borrowed, if_present) = match self {
638 MaskCheck::CheckNotBorrowed | MaskCheck::Borrow => (panic_bb, ok_bb),
639 MaskCheck::Return => (ok_bb, panic_bb),
640 };
641 ctx.builder()
642 .build_conditional_branch(bit, if_borrowed, if_present)?;
643 Ok(None)
644 },
645 )?;
646 Ok(())
647 }
648}
649
650struct BlockData<'c> {
651 block_ptr: PointerValue<'c>,
652 block: IntValue<'c>,
653 idx_in_block: IntValue<'c>,
654}
655
656fn inspect_mask_idx_bit<'c, H: HugrView<Node = Node>>(
657 ctx: &mut EmitFuncContext<'c, '_, H>,
658 mask_ptr: BasicValueEnum<'c>,
659 idx: BasicValueEnum<'c>,
660) -> Result<(BlockData<'c>, IntValue<'c>)> {
661 let usize_t = usize_ty(&ctx.typing_session());
662 let mask_ptr = mask_ptr.into_pointer_value();
663 let idx = idx.into_int_value();
664 let block_size = usize_t.const_int(usize_t.get_bit_width() as u64, false);
665 let builder = ctx.builder();
666 let block_idx = builder.build_int_unsigned_div(idx, block_size, "")?;
667 let block_ptr = unsafe { builder.build_in_bounds_gep(mask_ptr, &[block_idx], "")? };
668 let block = builder.build_load(block_ptr, "")?.into_int_value();
669 let idx_in_block = builder.build_int_unsigned_rem(idx, block_size, "")?;
670 let block_shifted = builder.build_right_shift(block, idx_in_block, false, "")?;
671 let bit = builder.build_int_truncate(block_shifted, ctx.iw_context().bool_type(), "")?;
672 Ok((
673 BlockData {
674 block_ptr,
675 block,
676 idx_in_block,
677 },
678 bit,
679 ))
680}
681
682struct MaskInfo<'a> {
683 mask_ptr: PointerValue<'a>,
684 offset: IntValue<'a>,
685 size: IntValue<'a>,
686}
687
688fn check_all_mask_eq<'c, H: HugrView<Node = Node>>(
691 ccg: &impl BorrowArrayCodegen,
692 ctx: &mut EmitFuncContext<'c, '_, H>,
693 mask_info: &MaskInfo<'c>,
694 expected: bool,
695 err: &ConstError,
696) -> Result<()> {
697 build_mask_padding1d(
698 ctx,
699 mask_info.mask_ptr,
700 mask_info.offset,
701 BitsToPad::Before,
702 expected,
703 )?;
704 let usize_t = usize_ty(&ctx.typing_session());
705 let builder = ctx.builder();
706 let last_idx = builder.build_int_sub(
707 builder.build_int_add(mask_info.offset, mask_info.size, "")?,
708 usize_t.const_int(1, false),
709 "last_valid",
710 )?;
711 build_mask_padding1d(
712 ctx,
713 mask_info.mask_ptr,
714 last_idx,
715 BitsToPad::After,
716 expected,
717 )?;
718
719 let builder = ctx.builder();
720 let expected_val = if expected {
721 usize_t.const_all_ones()
722 } else {
723 usize_t.const_zero()
724 };
725 let block_size = usize_t.const_int(usize_t.get_bit_width() as u64, false);
726 let start_block = builder.build_int_unsigned_div(mask_info.offset, block_size, "")?;
727 let end_block = builder.build_int_unsigned_div(last_idx, block_size, "")?;
728
729 let iters = builder.build_int_sub(end_block, start_block, "")?;
730 let iters = builder.build_int_add(iters, usize_t.const_int(1, false), "")?;
731 build_loop(ctx, iters, |ctx, idx| {
732 let builder = ctx.builder();
733 let block_idx = builder.build_int_add(idx, start_block, "")?;
734 let block_addr =
735 unsafe { builder.build_in_bounds_gep(mask_info.mask_ptr, &[block_idx], "")? };
736 let block = builder.build_load(block_addr, "")?.into_int_value();
737 let err_bb = ctx.build_positioned_new_block("mask_block_err", None, |ctx, bb| {
738 let err_val = ctx.emit_custom_const(err).unwrap();
739 ccg.emit_panic(ctx, err_val)?;
740 ctx.builder().build_unreachable()?;
741 Ok(bb)
742 })?;
743 let ok_bb = ctx.build_positioned_new_block("mask_block_ok", None, |_, bb| bb);
744 let builder = ctx.builder();
745 let cond = builder.build_int_compare(IntPredicate::EQ, block, expected_val, "")?;
746 builder.build_conditional_branch(cond, ok_bb, err_bb)?;
747 builder.position_at_end(ok_bb);
748 Ok(())
749 })
750}
751
752#[derive(Clone, Copy, Debug, PartialEq, Eq)]
753enum BitsToPad {
754 Before,
755 After,
756}
757
758fn build_mask_padding1d<'c, H: HugrView<Node = Node>>(
760 ctx: &mut EmitFuncContext<'c, '_, H>,
761 mask_ptr: PointerValue<'c>,
762 idx: IntValue<'c>,
763 end: BitsToPad,
764 value: bool,
765) -> Result<()> {
766 let builder = ctx.builder();
767 let usize_t = usize_ty(&ctx.typing_session());
768 let block_size = usize_t.const_int(usize_t.get_bit_width() as u64, false);
769
770 let block_idx = builder.build_int_unsigned_div(idx, block_size, "")?;
772 let block_addr = unsafe { builder.build_in_bounds_gep(mask_ptr, &[block_idx], "")? };
773 let block = builder.build_load(block_addr, "")?.into_int_value();
774
775 let all_ones = usize_t.const_all_ones();
776 let one = usize_t.const_int(1, false);
777 let idx_in_block = builder.build_int_unsigned_rem(idx, block_size, "")?;
778 let new_block = if value {
779 let (num_used, shifted) = match end {
781 BitsToPad::Before => {
782 let fst_block_used = builder.build_int_sub(block_size, idx_in_block, "")?;
783 let rsh = builder.build_right_shift(all_ones, fst_block_used, false, "")?;
784 (fst_block_used, rsh)
785 }
786 BitsToPad::After => {
787 let lst_block_used = builder.build_int_add(idx_in_block, one, "")?;
790 let lsh = builder.build_left_shift(all_ones, lst_block_used, "")?;
791 (lst_block_used, lsh)
792 }
793 };
794 let all_used = builder.build_int_compare(IntPredicate::EQ, num_used, block_size, "")?;
796 let pad = builder
797 .build_select(all_used, usize_t.const_zero(), shifted, "")?
798 .into_int_value();
799 builder.build_or(block, pad, "")?
800 } else {
801 let pad = match end {
803 BitsToPad::Before => builder.build_left_shift(all_ones, idx_in_block, "")?,
804 BitsToPad::After => {
805 let lst_block_used = builder.build_int_add(idx_in_block, one, "")?;
808 let lst_block_unused = builder.build_int_sub(block_size, lst_block_used, "")?;
809 builder.build_right_shift(all_ones, lst_block_unused, false, "")?
810 }
811 };
812 builder.build_and(block, pad, "")?
813 };
814 builder.build_store(block_addr, new_block)?;
815 Ok(())
816}
817
818pub fn build_is_borrowed_bit<'c, H: HugrView<Node = Node>>(
820 ctx: &mut EmitFuncContext<'c, '_, H>,
821 mask_ptr: PointerValue<'c>,
822 idx: IntValue<'c>,
823) -> Result<inkwell::values::IntValue<'c>> {
824 const FUNC_NAME: &str = "__barray_is_borrowed";
826 get_or_make_function(
827 ctx,
828 FUNC_NAME,
829 [mask_ptr.into(), idx.into()],
830 Some(ctx.iw_context().bool_type().into()),
831 |ctx, [mask_ptr, idx]| {
832 let (_, bit) = inspect_mask_idx_bit(ctx, mask_ptr, idx)?;
833 Ok(Some(bit.into()))
834 },
835 )
836 .map(|v| v.expect("i1 return value").into_int_value())
837}
838
839pub fn build_none_borrowed_check<'c, H: HugrView<Node = Node>>(
841 ccg: &impl BorrowArrayCodegen,
842 ctx: &mut EmitFuncContext<'c, '_, H>,
843 mask_ptr: PointerValue<'c>,
844 offset: IntValue<'c>,
845 size: u64,
846) -> Result<()> {
847 if size == 0 {
848 return Ok(());
849 }
850 const FUNC_NAME: &str = "__barray_check_none_borrowed";
852 let usize_t = usize_ty(&ctx.typing_session());
853 let size = usize_t.const_int(size, false);
854 get_or_make_function(
855 ctx,
856 FUNC_NAME,
857 [mask_ptr.into(), offset.into(), size.into()],
858 None,
859 |ctx, [mask_ptr, offset, size]| {
860 let mask_ptr = mask_ptr.into_pointer_value();
861 let offset = offset.into_int_value();
862 let size = size.into_int_value();
863 let info = MaskInfo {
865 mask_ptr,
866 offset,
867 size,
868 };
869 check_all_mask_eq(ccg, ctx, &info, false, &ERR_SOME_BORROWED)?;
870 Ok(None)
871 },
872 )?;
873 Ok(())
874}
875
876pub fn build_all_borrowed_check<'c, H: HugrView<Node = Node>>(
878 ccg: &impl BorrowArrayCodegen,
879 ctx: &mut EmitFuncContext<'c, '_, H>,
880 mask_ptr: PointerValue<'c>,
881 offset: IntValue<'c>,
882 size: u64,
883) -> Result<()> {
884 if size == 0 {
885 return Ok(());
886 }
887 const FUNC_NAME: &str = "__barray_check_all_borrowed";
889 let usize_t = usize_ty(&ctx.typing_session());
890 let size = usize_t.const_int(size, false);
891 get_or_make_function(
892 ctx,
893 FUNC_NAME,
894 [mask_ptr.into(), offset.into(), size.into()],
895 None,
896 |ctx, [mask_ptr, offset, size]| {
897 let mask_ptr = mask_ptr.into_pointer_value();
898 let offset = offset.into_int_value();
899 let size = size.into_int_value();
900 let info = MaskInfo {
902 mask_ptr,
903 offset,
904 size,
905 };
906 check_all_mask_eq(ccg, ctx, &info, true, &ERR_NOT_ALL_BORROWED)?;
907 Ok(None)
908 },
909 )?;
910 Ok(())
911}
912
913pub fn build_bounds_check<'c, H: HugrView<Node = Node>>(
915 ccg: &impl BorrowArrayCodegen,
916 ctx: &mut EmitFuncContext<'c, '_, H>,
917 size: u64,
918 idx: IntValue<'c>,
919) -> Result<()> {
920 const FUNC_NAME: &str = "__barray_check_bounds";
922 let size = usize_ty(&ctx.typing_session()).const_int(size, false);
923 get_or_make_function(
924 ctx,
925 FUNC_NAME,
926 [size.into(), idx.into()],
927 None,
928 |ctx, [size, idx]| {
929 let size = size.into_int_value();
930 let idx = idx.into_int_value();
931 let in_bounds = ctx
932 .builder()
933 .build_int_compare(IntPredicate::ULT, idx, size, "")?;
934 let ok_bb = ctx.build_positioned_new_block("ok", None, |_, bb| bb);
935 let err_bb = ctx.build_positioned_new_block("out_of_bounds", None, |ctx, bb| {
936 let err: &ConstError = &ERR_OUT_OF_BOUNDS;
937 let err_val = ctx.emit_custom_const(err).unwrap();
938 ccg.emit_panic(ctx, err_val)?;
939 ctx.builder().build_unreachable()?;
940 Ok(bb)
941 })?;
942 ctx.builder()
943 .build_conditional_branch(in_bounds, ok_bb, err_bb)?;
944 ctx.builder().position_at_end(ok_bb);
945 Ok(None)
946 },
947 )?;
948 Ok(())
949}
950
951fn build_loop<'c, T, H: HugrView<Node = Node>>(
956 ctx: &mut EmitFuncContext<'c, '_, H>,
957 iters: IntValue<'c>,
958 go: impl FnOnce(&mut EmitFuncContext<'c, '_, H>, IntValue<'c>) -> Result<T>,
959) -> Result<T> {
960 let builder = ctx.builder();
961 let idx_ty = usize_ty(&ctx.typing_session());
962 let idx_ptr = builder.build_alloca(idx_ty, "")?;
963 builder.build_store(idx_ptr, idx_ty.const_zero())?;
964 let exit_block = ctx.new_basic_block("", None);
965
966 let (body_start_block, body_end_block, val) =
967 ctx.build_positioned_new_block("", Some(exit_block), |ctx, body_start_bb| {
968 let idx = ctx.builder().build_load(idx_ptr, "")?.into_int_value();
969 let val = go(ctx, idx)?;
970 let builder = ctx.builder();
971 let body_end_bb = builder.get_insert_block().unwrap();
972 let inc_idx = builder.build_int_add(idx, idx_ty.const_int(1, false), "")?;
973 builder.build_store(idx_ptr, inc_idx)?;
974 Ok((body_start_bb, body_end_bb, val))
976 })?;
977
978 let head_block = ctx.build_positioned_new_block("", Some(body_start_block), |ctx, bb| {
979 let builder = ctx.builder();
980 let idx = builder.build_load(idx_ptr, "")?.into_int_value();
981 let cmp = builder.build_int_compare(IntPredicate::ULT, idx, iters, "")?;
982 builder.build_conditional_branch(cmp, body_start_block, exit_block)?;
983 Ok(bb)
984 })?;
985
986 let builder = ctx.builder();
987 builder.build_unconditional_branch(head_block)?;
988 builder.position_at_end(body_end_block);
989 builder.build_unconditional_branch(head_block)?;
990 ctx.builder().position_at_end(exit_block);
991 Ok(val)
992}
993
994pub fn emit_barray_value<'c, H: HugrView<Node = Node>>(
996 ccg: &impl BorrowArrayCodegen,
997 ctx: &mut EmitFuncContext<'c, '_, H>,
998 value: &borrow_array::BArrayValue,
999) -> Result<BasicValueEnum<'c>> {
1000 let ts = ctx.typing_session();
1001 let elem_ty = ts.llvm_type(value.get_element_type())?;
1002 let (elem_ptr, array_v) =
1003 build_barray_alloc(ctx, ccg, elem_ty, value.get_contents().len() as u64, false)?;
1004 for (i, v) in value.get_contents().iter().enumerate() {
1005 let llvm_v = emit_value(ctx, v)?;
1006 let idx = ts.iw_context().i32_type().const_int(i as u64, true);
1007 let elem_addr = unsafe { ctx.builder().build_in_bounds_gep(elem_ptr, &[idx], "")? };
1008 ctx.builder().build_store(elem_addr, llvm_v)?;
1009 }
1010 Ok(array_v.into())
1011}
1012
1013pub fn emit_barray_op<'c, H: HugrView<Node = Node>>(
1015 ccg: &impl BorrowArrayCodegen,
1016 ctx: &mut EmitFuncContext<'c, '_, H>,
1017 op: BArrayOp,
1018 inputs: Vec<BasicValueEnum<'c>>,
1019 outputs: RowPromise<'c>,
1020) -> Result<()> {
1021 let builder = ctx.builder();
1022 let ts = ctx.typing_session();
1023 let sig = op
1024 .clone()
1025 .to_extension_op()
1026 .unwrap()
1027 .signature()
1028 .into_owned();
1029 let BArrayOp {
1030 def,
1031 elem_ty: ref hugr_elem_ty,
1032 size,
1033 } = op;
1034 let elem_ty = ts.llvm_type(hugr_elem_ty)?;
1035 match def {
1036 BArrayOpDef::new_array => {
1037 let (elem_ptr, array_v) = build_barray_alloc(ctx, ccg, elem_ty, size, false)?;
1038 let usize_t = usize_ty(&ctx.typing_session());
1039 for (i, v) in inputs.into_iter().enumerate() {
1040 let idx = usize_t.const_int(i as u64, true);
1041 let elem_addr = unsafe { ctx.builder().build_in_bounds_gep(elem_ptr, &[idx], "")? };
1042 ctx.builder().build_store(elem_addr, v)?;
1043 }
1044 outputs.finish(ctx.builder(), [array_v.into()])
1045 }
1046 BArrayOpDef::unpack => {
1047 let [array_v] = inputs
1048 .try_into()
1049 .map_err(|_| anyhow!("BArrayOpDef::unpack expects one argument"))?;
1050 let BArrayFatPtrComponents {
1051 elems_ptr,
1052 mask_ptr,
1053 offset,
1054 } = decompose_barray_fat_pointer(builder, array_v)?;
1055
1056 let mut result = Vec::with_capacity(size as usize);
1057 let usize_t = usize_ty(&ctx.typing_session());
1058
1059 for i in 0..size {
1060 let idx = ctx
1061 .builder()
1062 .build_int_add(offset, usize_t.const_int(i, false), "")?;
1063 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, mask_ptr, idx)?;
1064 let elem_addr =
1065 unsafe { ctx.builder().build_in_bounds_gep(elems_ptr, &[idx], "")? };
1066 let elem_v = ctx.builder().build_load(elem_addr, "")?;
1067 result.push(elem_v);
1068 }
1069
1070 outputs.finish(ctx.builder(), result)
1071 }
1072 BArrayOpDef::get => {
1073 let [array_v, index_v] = inputs
1074 .try_into()
1075 .map_err(|_| anyhow!("BArrayOpDef::get expects two arguments"))?;
1076 let BArrayFatPtrComponents {
1077 elems_ptr,
1078 mask_ptr,
1079 offset,
1080 } = decompose_barray_fat_pointer(builder, array_v)?;
1081 let index_v = index_v.into_int_value();
1082 let res_hugr_ty = sig
1083 .output()
1084 .get(0)
1085 .ok_or(anyhow!("BArrayOp::get has no outputs"))?;
1086
1087 let res_sum_ty = {
1088 let TypeEnum::Sum(st) = res_hugr_ty.as_type_enum() else {
1089 Err(anyhow!("BArrayOp::get output is not a sum type"))?
1090 };
1091 ts.llvm_sum_type(st.clone())?
1092 };
1093
1094 let exit_rmb = ctx.new_row_mail_box(sig.output.iter(), "")?;
1095
1096 let exit_block = ctx.build_positioned_new_block("", None, |ctx, bb| {
1097 outputs.finish(ctx.builder(), exit_rmb.read_vec(ctx.builder(), [])?)?;
1098 Ok(bb)
1099 })?;
1100
1101 let success_block =
1102 ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
1103 let index_v = ctx.builder().build_int_add(index_v, offset, "")?;
1105 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, mask_ptr, index_v)?;
1106 let builder = ctx.builder();
1107 let elem_addr =
1108 unsafe { builder.build_in_bounds_gep(elems_ptr, &[index_v], "")? };
1109 let elem_v = builder.build_load(elem_addr, "")?;
1110 let success_v = res_sum_ty.build_tag(builder, 1, vec![elem_v])?;
1111 exit_rmb.write(ctx.builder(), [success_v.into(), array_v])?;
1112 builder.build_unconditional_branch(exit_block)?;
1113 Ok(bb)
1114 })?;
1115
1116 let failure_block =
1117 ctx.build_positioned_new_block("", Some(success_block), |ctx, bb| {
1118 let builder = ctx.builder();
1119 let failure_v = res_sum_ty.build_tag(builder, 0, vec![])?;
1120 exit_rmb.write(ctx.builder(), [failure_v.into(), array_v])?;
1121 builder.build_unconditional_branch(exit_block)?;
1122 Ok(bb)
1123 })?;
1124
1125 let builder = ctx.builder();
1126 let is_success = builder.build_int_compare(
1127 IntPredicate::ULT,
1128 index_v,
1129 index_v.get_type().const_int(size, false),
1130 "",
1131 )?;
1132
1133 builder.build_conditional_branch(is_success, success_block, failure_block)?;
1134 builder.position_at_end(exit_block);
1135 Ok(())
1136 }
1137 BArrayOpDef::set => {
1138 let [array_v, index_v, value_v] = inputs
1139 .try_into()
1140 .map_err(|_| anyhow!("BArrayOpDef::set expects three arguments"))?;
1141 let BArrayFatPtrComponents {
1142 elems_ptr,
1143 mask_ptr,
1144 offset,
1145 } = decompose_barray_fat_pointer(builder, array_v)?;
1146 let index_v = index_v.into_int_value();
1147
1148 let res_hugr_ty = sig
1149 .output()
1150 .get(0)
1151 .ok_or(anyhow!("BArrayOp::set has no outputs"))?;
1152
1153 let res_sum_ty = {
1154 let TypeEnum::Sum(st) = res_hugr_ty.as_type_enum() else {
1155 Err(anyhow!("BArrayOp::set output is not a sum type"))?
1156 };
1157 ts.llvm_sum_type(st.clone())?
1158 };
1159
1160 let exit_rmb = ctx.new_row_mail_box([res_hugr_ty], "")?;
1161
1162 let exit_block = ctx.build_positioned_new_block("", None, |ctx, bb| {
1163 outputs.finish(ctx.builder(), exit_rmb.read_vec(ctx.builder(), [])?)?;
1164 Ok(bb)
1165 })?;
1166
1167 let success_block =
1168 ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
1169 let index_v = ctx.builder().build_int_add(index_v, offset, "")?;
1171 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, mask_ptr, index_v)?;
1172 let builder = ctx.builder();
1173 let elem_addr =
1174 unsafe { builder.build_in_bounds_gep(elems_ptr, &[index_v], "")? };
1175 let elem_v = builder.build_load(elem_addr, "")?;
1176 builder.build_store(elem_addr, value_v)?;
1177 let success_v = res_sum_ty.build_tag(builder, 1, vec![elem_v, array_v])?;
1178 exit_rmb.write(ctx.builder(), [success_v.into()])?;
1179 builder.build_unconditional_branch(exit_block)?;
1180 Ok(bb)
1181 })?;
1182
1183 let failure_block =
1184 ctx.build_positioned_new_block("", Some(success_block), |ctx, bb| {
1185 let builder = ctx.builder();
1186 let failure_v = res_sum_ty.build_tag(builder, 0, vec![value_v, array_v])?;
1187 exit_rmb.write(ctx.builder(), [failure_v.into()])?;
1188 builder.build_unconditional_branch(exit_block)?;
1189 Ok(bb)
1190 })?;
1191
1192 let builder = ctx.builder();
1193 let is_success = builder.build_int_compare(
1194 IntPredicate::ULT,
1195 index_v,
1196 index_v.get_type().const_int(size, false),
1197 "",
1198 )?;
1199 builder.build_conditional_branch(is_success, success_block, failure_block)?;
1200 builder.position_at_end(exit_block);
1201 Ok(())
1202 }
1203 BArrayOpDef::swap => {
1204 let [array_v, index1_v, index2_v] = inputs
1205 .try_into()
1206 .map_err(|_| anyhow!("BArrayOpDef::swap expects three arguments"))?;
1207 let BArrayFatPtrComponents {
1208 elems_ptr,
1209 mask_ptr,
1210 offset,
1211 } = decompose_barray_fat_pointer(builder, array_v)?;
1212 let index1_v = index1_v.into_int_value();
1213 let index2_v = index2_v.into_int_value();
1214
1215 let res_hugr_ty = sig
1216 .output()
1217 .get(0)
1218 .ok_or(anyhow!("BArrayOp::swap has no outputs"))?;
1219
1220 let res_sum_ty = {
1221 let TypeEnum::Sum(st) = res_hugr_ty.as_type_enum() else {
1222 Err(anyhow!("BArrayOp::swap output is not a sum type"))?
1223 };
1224 ts.llvm_sum_type(st.clone())?
1225 };
1226
1227 let exit_rmb = ctx.new_row_mail_box([res_hugr_ty], "")?;
1228
1229 let exit_block = ctx.build_positioned_new_block("", None, |ctx, bb| {
1230 outputs.finish(ctx.builder(), exit_rmb.read_vec(ctx.builder(), [])?)?;
1231 Ok(bb)
1232 })?;
1233
1234 let success_block =
1235 ctx.build_positioned_new_block("", Some(exit_block), |ctx, bb| {
1236 let builder = ctx.builder();
1244 let index1_v = builder.build_int_add(index1_v, offset, "")?;
1247 let index2_v = builder.build_int_add(index2_v, offset, "")?;
1248 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, mask_ptr, index1_v)?;
1249 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, mask_ptr, index2_v)?;
1250 let builder = ctx.builder();
1251 let elem1_addr =
1252 unsafe { builder.build_in_bounds_gep(elems_ptr, &[index1_v], "")? };
1253 let elem1_v = builder.build_load(elem1_addr, "")?;
1254 let elem2_addr =
1255 unsafe { builder.build_in_bounds_gep(elems_ptr, &[index2_v], "")? };
1256 let elem2_v = builder.build_load(elem2_addr, "")?;
1257 builder.build_store(elem1_addr, elem2_v)?;
1258 builder.build_store(elem2_addr, elem1_v)?;
1259 let success_v = res_sum_ty.build_tag(builder, 1, vec![array_v])?;
1260 exit_rmb.write(ctx.builder(), [success_v.into()])?;
1261 builder.build_unconditional_branch(exit_block)?;
1262 Ok(bb)
1263 })?;
1264
1265 let failure_block =
1266 ctx.build_positioned_new_block("", Some(success_block), |ctx, bb| {
1267 let builder = ctx.builder();
1268 let failure_v = res_sum_ty.build_tag(builder, 0, vec![array_v])?;
1269 exit_rmb.write(ctx.builder(), [failure_v.into()])?;
1270 builder.build_unconditional_branch(exit_block)?;
1271 Ok(bb)
1272 })?;
1273
1274 let builder = ctx.builder();
1275 let is_success = {
1276 let index1_ok = builder.build_int_compare(
1277 IntPredicate::ULT,
1278 index1_v,
1279 index1_v.get_type().const_int(size, false),
1280 "",
1281 )?;
1282 let index2_ok = builder.build_int_compare(
1283 IntPredicate::ULT,
1284 index2_v,
1285 index2_v.get_type().const_int(size, false),
1286 "",
1287 )?;
1288 builder.build_and(index1_ok, index2_ok, "")?
1289 };
1290 builder.build_conditional_branch(is_success, success_block, failure_block)?;
1291 builder.position_at_end(exit_block);
1292 Ok(())
1293 }
1294 BArrayOpDef::pop_left => {
1295 let [array_v] = inputs
1296 .try_into()
1297 .map_err(|_| anyhow!("BArrayOpDef::pop_left expects one argument"))?;
1298 let r = emit_pop_op(
1299 ccg,
1300 ctx,
1301 hugr_elem_ty.clone(),
1302 size,
1303 array_v.into_struct_value(),
1304 true,
1305 )?;
1306 outputs.finish(ctx.builder(), [r])
1307 }
1308 BArrayOpDef::pop_right => {
1309 let [array_v] = inputs
1310 .try_into()
1311 .map_err(|_| anyhow!("BArrayOpDef::pop_right expects one argument"))?;
1312 let r = emit_pop_op(
1313 ccg,
1314 ctx,
1315 hugr_elem_ty.clone(),
1316 size,
1317 array_v.into_struct_value(),
1318 false,
1319 )?;
1320 outputs.finish(ctx.builder(), [r])
1321 }
1322 BArrayOpDef::discard_empty => {
1323 let [array_v] = inputs
1324 .try_into()
1325 .map_err(|_| anyhow!("BArrayOpDef::discard_empty expects one argument"))?;
1326 let BArrayFatPtrComponents {
1327 elems_ptr,
1328 mask_ptr,
1329 offset: _,
1330 } = decompose_barray_fat_pointer(builder, array_v)?;
1331 ccg.emit_free_array(ctx, elems_ptr)?;
1332 ccg.emit_free_array(ctx, mask_ptr)?;
1333 outputs.finish(ctx.builder(), [])
1334 }
1335 _ => todo!(),
1336 }
1337}
1338
1339pub fn emit_clone_op<'c, H: HugrView<Node = Node>>(
1341 ccg: &impl BorrowArrayCodegen,
1342 ctx: &mut EmitFuncContext<'c, '_, H>,
1343 op: BArrayClone,
1344 array_v: BasicValueEnum<'c>,
1345) -> Result<(BasicValueEnum<'c>, BasicValueEnum<'c>)> {
1346 let elem_ty = ctx.llvm_type(&op.elem_ty)?;
1347 let BArrayFatPtrComponents {
1348 elems_ptr,
1349 mask_ptr,
1350 offset,
1351 } = decompose_barray_fat_pointer(ctx.builder(), array_v)?;
1352 build_none_borrowed_check(ccg, ctx, mask_ptr, offset, op.size)?;
1353 let (other_ptr, other_array_v) = build_barray_alloc(ctx, ccg, elem_ty, op.size, false)?;
1354 let src_ptr = unsafe {
1355 ctx.builder()
1356 .build_in_bounds_gep(elems_ptr, &[offset], "")?
1357 };
1358 let length = usize_ty(&ctx.typing_session()).const_int(op.size, false);
1359 let size_value = ctx
1360 .builder()
1361 .build_int_mul(length, elem_ty.size_of().unwrap(), "")?;
1362 let is_volatile = ctx.iw_context().bool_type().const_zero();
1363
1364 let memcpy_intrinsic = Intrinsic::find("llvm.memcpy").unwrap();
1365 let memcpy = memcpy_intrinsic
1366 .get_declaration(
1367 ctx.get_current_module(),
1368 &[
1369 other_ptr.get_type().into(),
1370 src_ptr.get_type().into(),
1371 size_value.get_type().into(),
1372 ],
1373 )
1374 .unwrap();
1375 ctx.builder().build_call(
1376 memcpy,
1377 &[
1378 other_ptr.into(),
1379 src_ptr.into(),
1380 size_value.into(),
1381 is_volatile.into(),
1382 ],
1383 "",
1384 )?;
1385 Ok((array_v, other_array_v.into()))
1386}
1387
1388pub fn emit_barray_discard<'c, H: HugrView<Node = Node>>(
1390 ccg: &impl BorrowArrayCodegen,
1391 ctx: &mut EmitFuncContext<'c, '_, H>,
1392 _op: BArrayDiscard,
1393 array_v: BasicValueEnum<'c>,
1394) -> Result<()> {
1395 let array_ptr =
1396 ctx.builder()
1397 .build_extract_value(array_v.into_struct_value(), 0, "array_ptr")?;
1398 ccg.emit_free_array(ctx, array_ptr.into_pointer_value())?;
1399 Ok(())
1400}
1401
1402fn emit_pop_op<'c, H: HugrView<Node = Node>>(
1404 ccg: &impl BorrowArrayCodegen,
1405 ctx: &mut EmitFuncContext<'c, '_, H>,
1406 elem_ty: HugrType,
1407 size: u64,
1408 array_v: StructValue<'c>,
1409 pop_left: bool,
1410) -> Result<BasicValueEnum<'c>> {
1411 let ts = ctx.typing_session();
1412 let builder = ctx.builder();
1413 let fp = decompose_barray_fat_pointer(builder, array_v.into())?;
1414 let ret_ty = ts.llvm_sum_type(option_type(vec![
1415 elem_ty.clone(),
1416 borrow_array_type(size.saturating_add_signed(-1), elem_ty),
1417 ]))?;
1418 if size == 0 {
1419 return Ok(ret_ty.build_tag(builder, 0, vec![])?.into());
1420 }
1421 let (elem_ptr, new_array_offset) = {
1422 if pop_left {
1423 let new_array_offset = builder.build_int_add(
1424 fp.offset,
1425 usize_ty(&ts).const_int(1, false),
1426 "new_offset",
1427 )?;
1428 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, fp.mask_ptr, fp.offset)?;
1429 let elem_ptr = unsafe {
1430 ctx.builder()
1431 .build_in_bounds_gep(fp.elems_ptr, &[fp.offset], "")
1432 }?;
1433 (elem_ptr, new_array_offset)
1434 } else {
1435 let idx =
1436 builder.build_int_add(fp.offset, usize_ty(&ts).const_int(size - 1, false), "")?;
1437 MaskCheck::CheckNotBorrowed.emit(ccg, ctx, fp.mask_ptr, idx)?;
1438 let elem_ptr = unsafe { ctx.builder().build_in_bounds_gep(fp.elems_ptr, &[idx], "") }?;
1439 (elem_ptr, fp.offset)
1440 }
1441 };
1442 let elem_v = ctx.builder().build_load(elem_ptr, "")?;
1443 let new_array_v = build_barray_fat_pointer(
1444 ctx,
1445 BArrayFatPtrComponents {
1446 offset: new_array_offset,
1447 ..fp
1448 },
1449 )?;
1450
1451 Ok(ret_ty
1452 .build_tag(ctx.builder(), 1, vec![elem_v, new_array_v.into()])?
1453 .into())
1454}
1455
1456pub fn emit_repeat_op<'c, H: HugrView<Node = Node>>(
1458 ccg: &impl BorrowArrayCodegen,
1459 ctx: &mut EmitFuncContext<'c, '_, H>,
1460 op: BArrayRepeat,
1461 func: BasicValueEnum<'c>,
1462) -> Result<BasicValueEnum<'c>> {
1463 let elem_ty = ctx.llvm_type(&op.elem_ty)?;
1464 let (ptr, array_v) = build_barray_alloc(ctx, ccg, elem_ty, op.size, false)?;
1465 let array_len = usize_ty(&ctx.typing_session()).const_int(op.size, false);
1466 build_loop(ctx, array_len, |ctx, idx| {
1467 let builder = ctx.builder();
1468 let func_ptr = CallableValue::try_from(func.into_pointer_value())
1469 .map_err(|()| anyhow!("BArrayOpDef::repeat expects a function pointer"))?;
1470 let v = builder
1471 .build_call(func_ptr, &[], "")?
1472 .try_as_basic_value()
1473 .left()
1474 .ok_or(anyhow!("BArrayOpDef::repeat function must return a value"))?;
1475 let elem_addr = unsafe { builder.build_in_bounds_gep(ptr, &[idx], "")? };
1476 builder.build_store(elem_addr, v)?;
1477 Ok(())
1478 })?;
1479 Ok(array_v.into())
1480}
1481
1482pub fn emit_scan_op<'c, H: HugrView<Node = Node>>(
1486 ccg: &impl BorrowArrayCodegen,
1487 ctx: &mut EmitFuncContext<'c, '_, H>,
1488 op: BArrayScan,
1489 src_array_v: StructValue<'c>,
1490 func: BasicValueEnum<'c>,
1491 initial_accs: &[BasicValueEnum<'c>],
1492) -> Result<(BasicValueEnum<'c>, Vec<BasicValueEnum<'c>>)> {
1493 let BArrayFatPtrComponents {
1494 elems_ptr: src_ptr,
1495 mask_ptr: src_mask_ptr,
1496 offset: src_offset,
1497 } = decompose_barray_fat_pointer(ctx.builder(), src_array_v.into())?;
1498 build_none_borrowed_check(ccg, ctx, src_mask_ptr, src_offset, op.size)?;
1499 let tgt_elem_ty = ctx.llvm_type(&op.tgt_ty)?;
1500 let (tgt_ptr, tgt_array_v) = build_barray_alloc(ctx, ccg, tgt_elem_ty, op.size, false)?;
1503 let array_len = usize_ty(&ctx.typing_session()).const_int(op.size, false);
1504 let acc_tys: Vec<_> = op
1505 .acc_tys
1506 .iter()
1507 .map(|ty| ctx.llvm_type(ty))
1508 .try_collect()?;
1509 let builder = ctx.builder();
1510 let acc_ptrs: Vec<_> = acc_tys
1511 .iter()
1512 .map(|ty| builder.build_alloca(*ty, ""))
1513 .try_collect()?;
1514 for (ptr, initial_val) in acc_ptrs.iter().zip(initial_accs) {
1515 builder.build_store(*ptr, *initial_val)?;
1516 }
1517
1518 build_loop(ctx, array_len, |ctx, idx| {
1519 let builder = ctx.builder();
1520 let func_ptr = CallableValue::try_from(func.into_pointer_value())
1521 .map_err(|()| anyhow!("BArrayOpDef::scan expects a function pointer"))?;
1522 let src_idx = builder.build_int_add(idx, src_offset, "")?;
1523 let src_elem_addr = unsafe { builder.build_in_bounds_gep(src_ptr, &[src_idx], "")? };
1524 let src_elem = builder.build_load(src_elem_addr, "")?;
1525 let mut args = vec![src_elem.into()];
1526 for ptr in &acc_ptrs {
1527 args.push(builder.build_load(*ptr, "")?.into());
1528 }
1529 let call = builder.build_call(func_ptr, args.as_slice(), "")?;
1530 let call_results = deaggregate_call_result(builder, call, 1 + acc_tys.len())?;
1531 let tgt_elem_addr = unsafe { builder.build_in_bounds_gep(tgt_ptr, &[idx], "")? };
1532 builder.build_store(tgt_elem_addr, call_results[0])?;
1533 for (ptr, next_act) in acc_ptrs.iter().zip(call_results[1..].iter()) {
1534 builder.build_store(*ptr, *next_act)?;
1535 }
1536 Ok(())
1537 })?;
1538
1539 ccg.emit_free_array(ctx, src_ptr)?;
1540 ccg.emit_free_array(ctx, src_mask_ptr)?;
1541 let builder = ctx.builder();
1542 let final_accs = acc_ptrs
1543 .into_iter()
1544 .map(|ptr| builder.build_load(ptr, ""))
1545 .try_collect()?;
1546 Ok((tgt_array_v.into(), final_accs))
1547}
1548
1549pub fn emit_barray_unsafe_op<'c, H: HugrView<Node = Node>>(
1551 ccg: &impl BorrowArrayCodegen,
1552 ctx: &mut EmitFuncContext<'c, '_, H>,
1553 op: BArrayUnsafeOp,
1554 inputs: Vec<BasicValueEnum<'c>>,
1555 outputs: RowPromise<'c>,
1556) -> Result<()> {
1557 let builder = ctx.builder();
1558 let BArrayUnsafeOp {
1559 def,
1560 elem_ty: ref hugr_elem_ty,
1561 size,
1562 } = op;
1563
1564 match def {
1565 BArrayUnsafeOpDef::borrow => {
1566 let [array_v, index_v] = inputs
1567 .try_into()
1568 .map_err(|_| anyhow!("BArrayUnsafeOpDef::borrow expects two arguments"))?;
1569 let BArrayFatPtrComponents {
1570 elems_ptr,
1571 mask_ptr,
1572 offset,
1573 } = decompose_barray_fat_pointer(builder, array_v)?;
1574 let index_v = index_v.into_int_value();
1575 build_bounds_check(ccg, ctx, size, index_v)?;
1576 let offset_index_v = ctx.builder().build_int_add(index_v, offset, "")?;
1577 MaskCheck::Borrow.emit(ccg, ctx, mask_ptr, offset_index_v)?;
1578 let builder = ctx.builder();
1579 let elem_addr =
1580 unsafe { builder.build_in_bounds_gep(elems_ptr, &[offset_index_v], "")? };
1581 let elem_v = builder.build_load(elem_addr, "")?;
1582 outputs.finish(builder, [array_v, elem_v])
1583 }
1584 BArrayUnsafeOpDef::r#return => {
1585 let [array_v, index_v, elem_v] = inputs
1586 .try_into()
1587 .map_err(|_| anyhow!("BArrayUnsafeOpDef::return expects three arguments"))?;
1588 let BArrayFatPtrComponents {
1589 elems_ptr,
1590 mask_ptr,
1591 offset,
1592 } = decompose_barray_fat_pointer(builder, array_v)?;
1593 let index_v = index_v.into_int_value();
1594 build_bounds_check(ccg, ctx, size, index_v)?;
1595 let offset_index_v = ctx.builder().build_int_add(index_v, offset, "")?;
1596 MaskCheck::Return.emit(ccg, ctx, mask_ptr, offset_index_v)?;
1597 let builder = ctx.builder();
1598 let elem_addr =
1599 unsafe { builder.build_in_bounds_gep(elems_ptr, &[offset_index_v], "")? };
1600 builder.build_store(elem_addr, elem_v)?;
1601 outputs.finish(builder, [array_v])
1602 }
1603 BArrayUnsafeOpDef::discard_all_borrowed => {
1604 let [array_v] = inputs.try_into().map_err(|_| {
1605 anyhow!("BArrayUnsafeOpDef::discard_all_borrowed expects one argument")
1606 })?;
1607 let BArrayFatPtrComponents {
1608 elems_ptr,
1609 mask_ptr,
1610 offset,
1611 } = decompose_barray_fat_pointer(builder, array_v)?;
1612 build_all_borrowed_check(ccg, ctx, mask_ptr, offset, size)?;
1613 ccg.emit_free_array(ctx, elems_ptr)?;
1614 ccg.emit_free_array(ctx, mask_ptr)?;
1615 outputs.finish(ctx.builder(), [])
1616 }
1617 BArrayUnsafeOpDef::new_all_borrowed => {
1618 let elem_ty = ctx.llvm_type(hugr_elem_ty)?;
1619 let (_, array_v) = build_barray_alloc(ctx, ccg, elem_ty, size, true)?;
1620 outputs.finish(ctx.builder(), [array_v.into()])
1621 }
1622 BArrayUnsafeOpDef::is_borrowed => {
1623 let [array_v, index_v] = inputs
1624 .try_into()
1625 .map_err(|_| anyhow!("BArrayUnsafeOpDef::is_borrowed expects two arguments"))?;
1626 let BArrayFatPtrComponents {
1627 mask_ptr, offset, ..
1628 } = decompose_barray_fat_pointer(builder, array_v)?;
1629 let index_v = index_v.into_int_value();
1630 build_bounds_check(ccg, ctx, size, index_v)?;
1631 let offset_index_v = ctx.builder().build_int_add(index_v, offset, "")?;
1632 let bit = build_is_borrowed_bit(ctx, mask_ptr, offset_index_v)?;
1634 outputs.finish(ctx.builder(), [array_v, bit.into()])
1635 }
1636 _ => todo!(),
1637 }
1638}
1639
1640pub fn emit_to_array_op<'c, H: HugrView<Node = Node>>(
1642 ccg: &impl BorrowArrayCodegen,
1643 ctx: &mut EmitFuncContext<'c, '_, H>,
1644 op: BArrayToArray,
1645 barray_v: BasicValueEnum<'c>,
1646) -> Result<BasicValueEnum<'c>> {
1647 let BArrayFatPtrComponents {
1648 elems_ptr,
1649 mask_ptr,
1650 offset,
1651 } = decompose_barray_fat_pointer(ctx.builder(), barray_v)?;
1652 build_none_borrowed_check(ccg, ctx, mask_ptr, offset, op.size)?;
1653 Ok(build_array_fat_pointer(ctx, elems_ptr, offset)?.into())
1654}
1655
1656pub fn emit_from_array_op<'c, H: HugrView<Node = Node>>(
1658 ccg: &impl BorrowArrayCodegen,
1659 ctx: &mut EmitFuncContext<'c, '_, H>,
1660 op: BArrayFromArray,
1661 array_v: BasicValueEnum<'c>,
1662) -> Result<BasicValueEnum<'c>> {
1663 let usize_t = usize_ty(&ctx.typing_session());
1666 let builder = ctx.builder();
1667 let (ptr, offset) = decompose_array_fat_pointer(builder, array_v)?;
1668 let size = usize_t.const_int(op.size, false);
1669 let mask_bits = builder.build_int_add(size, offset, "")?;
1670 let mask_blocks = builder.build_int_unsigned_div(mask_bits, usize_t.size_of(), "")?;
1671 let mask_blocks = builder.build_int_add(mask_blocks, usize_t.const_int(1, false), "")?;
1673 let (mask_ptr, mask_size) = alloc_typed_array(ccg, ctx, usize_t.into(), mask_blocks)?;
1674 fill_mask(ctx, mask_ptr, mask_size, false)?;
1675 Ok(build_barray_fat_pointer(
1676 ctx,
1677 BArrayFatPtrComponents {
1678 elems_ptr: ptr,
1679 mask_ptr,
1680 offset,
1681 },
1682 )?
1683 .into())
1684}
1685
1686#[cfg(test)]
1687mod test {
1688 use hugr_core::Wire;
1689 use hugr_core::builder::{DataflowHugr, HugrBuilder};
1690 use hugr_core::extension::prelude::either_type;
1691 use hugr_core::ops::Tag;
1692 use hugr_core::std_extensions::STD_REG;
1693 use hugr_core::std_extensions::arithmetic::conversions::ConvertOpDef;
1694 use hugr_core::std_extensions::arithmetic::int_ops::IntOpDef;
1695 use hugr_core::std_extensions::collections::array::ArrayOpBuilder;
1696 use hugr_core::std_extensions::collections::array::op_builder::build_all_borrow_array_ops;
1697 use hugr_core::std_extensions::collections::borrow_array::{
1698 self, BArrayOpBuilder, BArrayRepeat, BArrayScan, BArrayToArray, borrow_array_type,
1699 };
1700 use hugr_core::types::Type;
1701 use hugr_core::{
1702 builder::{Dataflow, DataflowSubContainer, SubContainer},
1703 extension::{
1704 ExtensionRegistry,
1705 prelude::{self, ConstUsize, UnwrapBuilder as _, bool_t, option_type, usize_t},
1706 },
1707 ops::Value,
1708 std_extensions::{
1709 arithmetic::{
1710 int_ops::{self},
1711 int_types::{self, ConstInt, int_type},
1712 },
1713 logic,
1714 },
1715 type_row,
1716 types::Signature,
1717 };
1718 use itertools::Itertools as _;
1719 use rstest::rstest;
1720
1721 use crate::emit::test::PanicTestPreludeCodegen;
1722 use crate::extension::DefaultPreludeCodegen;
1723 use crate::types::HugrType;
1724 use crate::{
1725 check_emission,
1726 emit::test::SimpleHugrConfig,
1727 test::{TestContext, exec_ctx, llvm_ctx},
1728 utils::{IntOpBuilder, LogicOpBuilder},
1729 };
1730
1731 #[rstest]
1732 fn emit_all_ops(mut llvm_ctx: TestContext) {
1733 let hugr = SimpleHugrConfig::new()
1734 .with_extensions(STD_REG.to_owned())
1735 .finish(|mut builder| {
1736 build_all_borrow_array_ops(builder.dfg_builder_endo([]).unwrap())
1737 .finish_sub_container()
1738 .unwrap();
1739 builder.finish_hugr().unwrap()
1740 });
1741 llvm_ctx.add_extensions(|cge| {
1742 cge.add_default_prelude_extensions()
1743 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
1744 });
1745 check_emission!(hugr, llvm_ctx);
1746 }
1747
1748 #[rstest]
1749 fn emit_get(mut llvm_ctx: TestContext) {
1750 let hugr = SimpleHugrConfig::new()
1751 .with_extensions(STD_REG.to_owned())
1752 .finish(|mut builder| {
1753 let us1 = builder.add_load_value(ConstUsize::new(1));
1754 let us2 = builder.add_load_value(ConstUsize::new(2));
1755 let arr = builder.add_new_borrow_array(usize_t(), [us1, us2]).unwrap();
1756 let (_, arr) = builder
1757 .add_borrow_array_get(usize_t(), 2, arr, us1)
1758 .unwrap();
1759 builder.add_borrow_array_discard(usize_t(), 2, arr).unwrap();
1760 builder.finish_hugr_with_outputs([]).unwrap()
1761 });
1762 llvm_ctx.add_extensions(|cge| {
1763 cge.add_default_prelude_extensions()
1764 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
1765 });
1766 check_emission!(hugr, llvm_ctx);
1767 }
1768
1769 #[rstest]
1770 fn emit_clone(mut llvm_ctx: TestContext) {
1771 let hugr = SimpleHugrConfig::new()
1772 .with_extensions(STD_REG.to_owned())
1773 .finish(|mut builder| {
1774 let us1 = builder.add_load_value(ConstUsize::new(1));
1775 let us2 = builder.add_load_value(ConstUsize::new(2));
1776 let arr = builder.add_new_borrow_array(usize_t(), [us1, us2]).unwrap();
1777 let (arr1, arr2) = builder.add_borrow_array_clone(usize_t(), 2, arr).unwrap();
1778 builder
1779 .add_borrow_array_discard(usize_t(), 2, arr1)
1780 .unwrap();
1781 builder
1782 .add_borrow_array_discard(usize_t(), 2, arr2)
1783 .unwrap();
1784 builder.finish_hugr_with_outputs([]).unwrap()
1785 });
1786 llvm_ctx.add_extensions(|cge| {
1787 cge.add_default_prelude_extensions()
1788 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
1789 });
1790 check_emission!(hugr, llvm_ctx);
1791 }
1792
1793 #[rstest]
1794 fn emit_array_value(mut llvm_ctx: TestContext) {
1795 let hugr = SimpleHugrConfig::new()
1796 .with_extensions(STD_REG.to_owned())
1797 .with_outs(vec![borrow_array_type(2, usize_t())])
1798 .finish(|mut builder| {
1799 let vs = vec![ConstUsize::new(1).into(), ConstUsize::new(2).into()];
1800 let arr = builder.add_load_value(borrow_array::BArrayValue::new(usize_t(), vs));
1801 builder.finish_hugr_with_outputs([arr]).unwrap()
1802 });
1803 llvm_ctx.add_extensions(|cge| {
1804 cge.add_default_prelude_extensions()
1805 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
1806 });
1807 check_emission!(hugr, llvm_ctx);
1808 }
1809
1810 fn exec_registry() -> ExtensionRegistry {
1846 ExtensionRegistry::new([
1847 int_types::EXTENSION.to_owned(),
1848 int_ops::EXTENSION.to_owned(),
1849 logic::EXTENSION.to_owned(),
1850 prelude::PRELUDE.to_owned(),
1851 borrow_array::EXTENSION.to_owned(),
1852 ])
1853 }
1854
1855 #[rstest]
1856 #[case(0, 1)]
1857 #[case(1, 2)]
1858 #[case(3, 0)]
1859 #[case(999999, 0)]
1860 fn exec_get(mut exec_ctx: TestContext, #[case] index: u64, #[case] expected: u64) {
1861 let hugr = SimpleHugrConfig::new()
1866 .with_outs(usize_t())
1867 .with_extensions(exec_registry())
1868 .finish(|mut builder| {
1869 let us0 = builder.add_load_value(ConstUsize::new(0));
1870 let us1 = builder.add_load_value(ConstUsize::new(1));
1871 let us2 = builder.add_load_value(ConstUsize::new(2));
1872 let arr = builder.add_new_borrow_array(usize_t(), [us1, us2]).unwrap();
1873 let i = builder.add_load_value(ConstUsize::new(index));
1874 let (get_r, arr) = builder.add_borrow_array_get(usize_t(), 2, arr, i).unwrap();
1875 builder.add_borrow_array_discard(usize_t(), 2, arr).unwrap();
1876 let r = {
1877 let ot = option_type(usize_t());
1878 let variants = (0..ot.num_variants())
1879 .map(|i| ot.get_variant(i).cloned().unwrap().try_into().unwrap())
1880 .collect_vec();
1881 let mut builder = builder
1882 .conditional_builder((variants, get_r), [], usize_t().into())
1883 .unwrap();
1884 {
1885 let failure_case = builder.case_builder(0).unwrap();
1886 failure_case.finish_with_outputs([us0]).unwrap();
1887 }
1888 {
1889 let success_case = builder.case_builder(1).unwrap();
1890 let inputs = success_case.input_wires();
1891 success_case.finish_with_outputs(inputs).unwrap();
1892 }
1893 builder.finish_sub_container().unwrap().out_wire(0)
1894 };
1895 builder.finish_hugr_with_outputs([r]).unwrap()
1896 });
1897 exec_ctx.add_extensions(|cge| {
1898 cge.add_default_prelude_extensions()
1899 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
1900 });
1901 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
1902 }
1903
1904 #[rstest]
1905 #[case(0, 3, 1, [3,2])]
1906 #[case(1, 3, 2, [1,3])]
1907 #[case(2, 3, 3, [1,2])]
1908 #[case(999999, 3, 3, [1,2])]
1909 fn exec_set(
1910 mut exec_ctx: TestContext,
1911 #[case] index: u64,
1912 #[case] value: u64,
1913 #[case] expected_elem: u64,
1914 #[case] expected_arr: [u64; 2],
1915 ) {
1916 use hugr_core::extension::prelude::either_type;
1924 let int_ty = int_type(3);
1925 let hugr = SimpleHugrConfig::new()
1926 .with_outs(usize_t())
1927 .with_extensions(exec_registry())
1928 .finish(|mut builder| {
1929 let us0 = builder.add_load_value(ConstUsize::new(0));
1930 let us1 = builder.add_load_value(ConstUsize::new(1));
1931 let i1 = builder.add_load_value(ConstInt::new_u(3, 1).unwrap());
1932 let i2 = builder.add_load_value(ConstInt::new_u(3, 2).unwrap());
1933 let arr = builder
1934 .add_new_borrow_array(int_ty.clone(), [i1, i2])
1935 .unwrap();
1936 let index = builder.add_load_value(ConstUsize::new(index));
1937 let value = builder.add_load_value(ConstInt::new_u(3, value).unwrap());
1938 let get_r = builder
1939 .add_borrow_array_set(int_ty.clone(), 2, arr, index, value)
1940 .unwrap();
1941 let r = {
1942 let res_sum_ty = {
1943 let row = vec![int_ty.clone(), borrow_array_type(2, int_ty.clone())];
1944 either_type(row.clone(), row)
1945 };
1946 let variants = (0..res_sum_ty.num_variants())
1947 .map(|i| {
1948 res_sum_ty
1949 .get_variant(i)
1950 .cloned()
1951 .unwrap()
1952 .try_into()
1953 .unwrap()
1954 })
1955 .collect_vec();
1956 let mut builder = builder
1957 .conditional_builder((variants, get_r), [], bool_t().into())
1958 .unwrap();
1959 for i in 0..2 {
1960 let mut builder = builder.case_builder(i).unwrap();
1961 let [elem, arr] = builder.input_wires_arr();
1962 let expected_elem =
1963 builder.add_load_value(ConstInt::new_u(3, expected_elem).unwrap());
1964 let expected_arr_0 =
1965 builder.add_load_value(ConstInt::new_u(3, expected_arr[0]).unwrap());
1966 let expected_arr_1 =
1967 builder.add_load_value(ConstInt::new_u(3, expected_arr[1]).unwrap());
1968 let (r, arr) = builder
1969 .add_borrow_array_get(int_ty.clone(), 2, arr, us0)
1970 .unwrap();
1971 let [arr_0] = builder
1972 .build_unwrap_sum(1, option_type(int_ty.clone()), r)
1973 .unwrap();
1974 let (r, arr) = builder
1975 .add_borrow_array_get(int_ty.clone(), 2, arr, us1)
1976 .unwrap();
1977 let [arr_1] = builder
1978 .build_unwrap_sum(1, option_type(int_ty.clone()), r)
1979 .unwrap();
1980 let elem_eq = builder.add_ieq(3, elem, expected_elem).unwrap();
1981 let arr_0_eq = builder.add_ieq(3, arr_0, expected_arr_0).unwrap();
1982 let arr_1_eq = builder.add_ieq(3, arr_1, expected_arr_1).unwrap();
1983 let r = builder.add_and(elem_eq, arr_0_eq).unwrap();
1984 let r = builder.add_and(r, arr_1_eq).unwrap();
1985 builder
1986 .add_borrow_array_discard(int_ty.clone(), 2, arr)
1987 .unwrap();
1988 builder.finish_with_outputs([r]).unwrap();
1989 }
1990 builder.finish_sub_container().unwrap().out_wire(0)
1991 };
1992 let r = {
1993 let mut conditional = builder
1994 .conditional_builder(([type_row![], type_row![]], r), [], usize_t().into())
1995 .unwrap();
1996 conditional
1997 .case_builder(0)
1998 .unwrap()
1999 .finish_with_outputs([us0])
2000 .unwrap();
2001 conditional
2002 .case_builder(1)
2003 .unwrap()
2004 .finish_with_outputs([us1])
2005 .unwrap();
2006 conditional.finish_sub_container().unwrap().out_wire(0)
2007 };
2008 builder.finish_hugr_with_outputs([r]).unwrap()
2009 });
2010 exec_ctx.add_extensions(|cge| {
2011 cge.add_default_prelude_extensions()
2012 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2013 .add_default_int_extensions()
2014 .add_logic_extensions()
2015 });
2016 assert_eq!(1, exec_ctx.exec_hugr_u64(hugr, "main"));
2017 }
2018
2019 #[rstest]
2020 #[case(0, 1, [2,1], true)]
2021 #[case(0, 0, [1,2], true)]
2022 #[case(0, 2, [1,2], false)]
2023 #[case(2, 0, [1,2], false)]
2024 #[case(9999999, 0, [1,2], false)]
2025 #[case(0, 9999999, [1,2], false)]
2026 fn exec_swap(
2027 mut exec_ctx: TestContext,
2028 #[case] index1: u64,
2029 #[case] index2: u64,
2030 #[case] expected_arr: [u64; 2],
2031 #[case] expected_succeeded: bool,
2032 ) {
2033 let int_ty = int_type(3);
2041 let arr_ty = borrow_array_type(2, int_ty.clone());
2042 let hugr = SimpleHugrConfig::new()
2043 .with_outs(usize_t())
2044 .with_extensions(exec_registry())
2045 .finish(|mut builder| {
2046 let us0 = builder.add_load_value(ConstUsize::new(0));
2047 let us1 = builder.add_load_value(ConstUsize::new(1));
2048 let i1 = builder.add_load_value(ConstInt::new_u(3, 1).unwrap());
2049 let i2 = builder.add_load_value(ConstInt::new_u(3, 2).unwrap());
2050 let arr = builder
2051 .add_new_borrow_array(int_ty.clone(), [i1, i2])
2052 .unwrap();
2053
2054 let index1 = builder.add_load_value(ConstUsize::new(index1));
2055 let index2 = builder.add_load_value(ConstUsize::new(index2));
2056 let r = builder
2057 .add_borrow_array_swap(int_ty.clone(), 2, arr, index1, index2)
2058 .unwrap();
2059 let [arr, was_expected_success] = {
2060 let mut conditional = builder
2061 .conditional_builder(
2062 (
2063 [vec![arr_ty.clone()].into(), vec![arr_ty.clone()].into()],
2064 r,
2065 ),
2066 [],
2067 vec![arr_ty, bool_t()].into(),
2068 )
2069 .unwrap();
2070 for i in 0..2 {
2071 let mut case = conditional.case_builder(i).unwrap();
2072 let [arr] = case.input_wires_arr();
2073 let was_expected_success =
2074 case.add_load_value(if (i == 1) == expected_succeeded {
2075 Value::true_val()
2076 } else {
2077 Value::false_val()
2078 });
2079 case.finish_with_outputs([arr, was_expected_success])
2080 .unwrap();
2081 }
2082 conditional.finish_sub_container().unwrap().outputs_arr()
2083 };
2084 let (r, arr) = builder
2085 .add_borrow_array_get(int_ty.clone(), 2, arr, us0)
2086 .unwrap();
2087 let elem_0 = builder
2088 .build_unwrap_sum::<1>(1, option_type(int_ty.clone()), r)
2089 .unwrap()[0];
2090 let (r, arr) = builder
2091 .add_borrow_array_get(int_ty.clone(), 2, arr, us1)
2092 .unwrap();
2093 let elem_1 = builder
2094 .build_unwrap_sum::<1>(1, option_type(int_ty.clone()), r)
2095 .unwrap()[0];
2096 let expected_elem_0 =
2097 builder.add_load_value(ConstInt::new_u(3, expected_arr[0]).unwrap());
2098 let elem_0_ok = builder.add_ieq(3, elem_0, expected_elem_0).unwrap();
2099 let expected_elem_1 =
2100 builder.add_load_value(ConstInt::new_u(3, expected_arr[1]).unwrap());
2101 let elem_1_ok = builder.add_ieq(3, elem_1, expected_elem_1).unwrap();
2102 let r = builder.add_and(was_expected_success, elem_0_ok).unwrap();
2103 let r = builder.add_and(r, elem_1_ok).unwrap();
2104 let r = {
2105 let mut conditional = builder
2106 .conditional_builder(([type_row![], type_row![]], r), [], usize_t().into())
2107 .unwrap();
2108 conditional
2109 .case_builder(0)
2110 .unwrap()
2111 .finish_with_outputs([us0])
2112 .unwrap();
2113 conditional
2114 .case_builder(1)
2115 .unwrap()
2116 .finish_with_outputs([us1])
2117 .unwrap();
2118 conditional.finish_sub_container().unwrap().out_wire(0)
2119 };
2120 builder
2121 .add_borrow_array_discard(int_ty.clone(), 2, arr)
2122 .unwrap();
2123 builder.finish_hugr_with_outputs([r]).unwrap()
2124 });
2125 exec_ctx.add_extensions(|cge| {
2126 cge.add_default_prelude_extensions()
2127 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2128 .add_default_int_extensions()
2129 .add_logic_extensions()
2130 });
2131 assert_eq!(1, exec_ctx.exec_hugr_u64(hugr, "main"));
2132 }
2133
2134 #[rstest]
2135 #[case(0, 5)]
2136 #[case(1, 5)]
2137 fn exec_clone(mut exec_ctx: TestContext, #[case] index: u64, #[case] new_v: u64) {
2138 let int_ty = int_type(3);
2145 let arr_ty = borrow_array_type(2, int_ty.clone());
2146 let hugr = SimpleHugrConfig::new()
2147 .with_outs(int_ty.clone())
2148 .with_extensions(exec_registry())
2149 .finish(|mut builder| {
2150 let idx = builder.add_load_value(ConstUsize::new(index));
2151 let i1 = builder.add_load_value(ConstInt::new_u(3, 1).unwrap());
2152 let i2 = builder.add_load_value(ConstInt::new_u(3, 2).unwrap());
2153 let inew = builder.add_load_value(ConstInt::new_u(3, new_v).unwrap());
2154 let arr = builder
2155 .add_new_borrow_array(int_ty.clone(), [i1, i2])
2156 .unwrap();
2157
2158 let (arr, arr_clone) = builder
2159 .add_borrow_array_clone(int_ty.clone(), 2, arr)
2160 .unwrap();
2161 let r = builder
2162 .add_borrow_array_set(int_ty.clone(), 2, arr, idx, inew)
2163 .unwrap();
2164 let [_, arr] = builder
2165 .build_unwrap_sum(
2166 1,
2167 either_type(
2168 vec![int_ty.clone(), arr_ty.clone()],
2169 vec![int_ty.clone(), arr_ty.clone()],
2170 ),
2171 r,
2172 )
2173 .unwrap();
2174 let (r, arr_clone) = builder
2175 .add_borrow_array_get(int_ty.clone(), 2, arr_clone, idx)
2176 .unwrap();
2177 let [elem] = builder
2178 .build_unwrap_sum(1, option_type(int_ty.clone()), r)
2179 .unwrap();
2180 builder
2181 .add_borrow_array_discard(int_ty.clone(), 2, arr)
2182 .unwrap();
2183 builder
2184 .add_borrow_array_discard(int_ty.clone(), 2, arr_clone)
2185 .unwrap();
2186 builder.finish_hugr_with_outputs([elem]).unwrap()
2187 });
2188 exec_ctx.add_extensions(|cge| {
2189 cge.add_default_prelude_extensions()
2190 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2191 .add_default_int_extensions()
2192 .add_logic_extensions()
2193 });
2194 assert_eq!([1, 2][index as usize], exec_ctx.exec_hugr_u64(hugr, "main"));
2195 }
2196
2197 #[rstest]
2198 #[case(&[], 0)]
2199 #[case(&[true], 1)]
2200 #[case(&[false], 4)]
2201 #[case(&[true, true], 3)]
2202 #[case(&[false, false], 6)]
2203 #[case(&[true, false, true], 7)]
2204 #[case(&[false, true, false], 7)]
2205 fn exec_pop(mut exec_ctx: TestContext, #[case] from_left: &[bool], #[case] expected: u64) {
2206 let array_contents = [1, 2, 4];
2212 let int_ty = int_type(6);
2213 let hugr = SimpleHugrConfig::new()
2214 .with_outs(int_ty.clone())
2215 .with_extensions(exec_registry())
2216 .finish(|mut builder| {
2217 let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2218 let new_array_args = array_contents
2219 .iter()
2220 .map(|&i| builder.add_load_value(ConstInt::new_u(6, i).unwrap()))
2221 .collect_vec();
2222 let mut arr = builder
2223 .add_new_borrow_array(int_ty.clone(), new_array_args)
2224 .unwrap();
2225 for (i, left) in from_left.iter().enumerate() {
2226 let array_size = (array_contents.len() - i) as u64;
2227 let pop_res = if *left {
2228 builder
2229 .add_borrow_array_pop_left(int_ty.clone(), array_size, arr)
2230 .unwrap()
2231 } else {
2232 builder
2233 .add_borrow_array_pop_right(int_ty.clone(), array_size, arr)
2234 .unwrap()
2235 };
2236 let [elem, new_arr] = builder
2237 .build_unwrap_sum(
2238 1,
2239 option_type(vec![
2240 int_ty.clone(),
2241 borrow_array_type(array_size - 1, int_ty.clone()),
2242 ]),
2243 pop_res,
2244 )
2245 .unwrap();
2246 arr = new_arr;
2247 r = builder.add_iadd(6, r, elem).unwrap();
2248 }
2249 builder
2250 .add_borrow_array_discard(
2251 int_ty.clone(),
2252 (array_contents.len() - from_left.len()) as u64,
2253 arr,
2254 )
2255 .unwrap();
2256 builder.finish_hugr_with_outputs([r]).unwrap()
2257 });
2258 exec_ctx.add_extensions(|cge| {
2259 cge.add_default_prelude_extensions()
2260 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2261 .add_default_int_extensions()
2262 });
2263 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2264 }
2265
2266 #[rstest]
2267 #[case(&[], 0)]
2268 #[case(&[1, 2], 3)]
2269 #[case(&[6, 6, 6], 18)]
2270 fn exec_unpack(
2271 mut exec_ctx: TestContext,
2272 #[case] array_contents: &[u64],
2273 #[case] expected: u64,
2274 ) {
2275 let int_ty = int_type(6);
2281 let hugr = SimpleHugrConfig::new()
2282 .with_outs(int_ty.clone())
2283 .with_extensions(exec_registry())
2284 .finish(|mut builder| {
2285 let array = borrow_array::BArrayValue::new(
2286 int_ty.clone(),
2287 array_contents
2288 .iter()
2289 .map(|&i| ConstInt::new_u(6, i).unwrap().into())
2290 .collect_vec(),
2291 );
2292 let array = builder.add_load_value(array);
2293 let unpacked = builder
2294 .add_borrow_array_unpack(int_ty.clone(), array_contents.len() as u64, array)
2295 .unwrap();
2296 let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2297 for elem in unpacked {
2298 r = builder.add_iadd(6, r, elem).unwrap();
2299 }
2300
2301 builder.finish_hugr_with_outputs([r]).unwrap()
2302 });
2303 exec_ctx.add_extensions(|cge| {
2304 cge.add_default_prelude_extensions()
2305 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2306 .add_default_int_extensions()
2307 });
2308 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2309 }
2310
2311 #[rstest]
2312 #[case(5, 42, 0)]
2313 #[case(5, 42, 1)]
2314 #[case(5, 42, 2)]
2315 #[case(5, 42, 3)]
2316 #[case(5, 42, 4)]
2317 fn exec_repeat(
2318 mut exec_ctx: TestContext,
2319 #[case] size: u64,
2320 #[case] value: u64,
2321 #[case] idx: u64,
2322 ) {
2323 let int_ty = int_type(6);
2329 let hugr = SimpleHugrConfig::new()
2330 .with_outs(int_ty.clone())
2331 .with_extensions(exec_registry())
2332 .finish(|mut builder| {
2333 let mut mb = builder.module_root_builder();
2334 let mut func = mb
2335 .define_function("foo", Signature::new(vec![], vec![int_ty.clone()]))
2336 .unwrap();
2337 let v = func.add_load_value(ConstInt::new_u(6, value).unwrap());
2338 let func_id = func.finish_with_outputs(vec![v]).unwrap();
2339 let func_v = builder.load_func(func_id.handle(), &[]).unwrap();
2340 let repeat = BArrayRepeat::new(int_ty.clone(), size);
2341 let arr = builder
2342 .add_dataflow_op(repeat, vec![func_v])
2343 .unwrap()
2344 .out_wire(0);
2345 let idx_v = builder.add_load_value(ConstUsize::new(idx));
2346 let (get_res, arr) = builder
2347 .add_borrow_array_get(int_ty.clone(), size, arr, idx_v)
2348 .unwrap();
2349 let [elem] = builder
2350 .build_unwrap_sum(1, option_type(vec![int_ty.clone()]), get_res)
2351 .unwrap();
2352 builder
2353 .add_borrow_array_discard(int_ty.clone(), size, arr)
2354 .unwrap();
2355 builder.finish_hugr_with_outputs([elem]).unwrap()
2356 });
2357 exec_ctx.add_extensions(|cge| {
2358 cge.add_default_prelude_extensions()
2359 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2360 .add_default_int_extensions()
2361 });
2362 assert_eq!(value, exec_ctx.exec_hugr_u64(hugr, "main"));
2363 }
2364
2365 #[rstest]
2366 #[case(10, 1)]
2367 #[case(10, 2)]
2368 #[case(0, 1)]
2369 fn exec_scan_map(mut exec_ctx: TestContext, #[case] size: u64, #[case] inc: u64) {
2370 let int_ty = int_type(6);
2376 let hugr = SimpleHugrConfig::new()
2377 .with_outs(int_ty.clone())
2378 .with_extensions(exec_registry())
2379 .finish(|mut builder| {
2380 let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2381 let new_array_args = (0..size)
2382 .map(|i| builder.add_load_value(ConstInt::new_u(6, i).unwrap()))
2383 .collect_vec();
2384 let arr = builder
2385 .add_new_borrow_array(int_ty.clone(), new_array_args)
2386 .unwrap();
2387
2388 let mut mb = builder.module_root_builder();
2389 let mut func = mb
2390 .define_function(
2391 "foo",
2392 Signature::new(vec![int_ty.clone()], vec![int_ty.clone()]),
2393 )
2394 .unwrap();
2395 let [elem] = func.input_wires_arr();
2396 let delta = func.add_load_value(ConstInt::new_u(6, inc).unwrap());
2397 let out = func.add_iadd(6, elem, delta).unwrap();
2398 let func_id = func.finish_with_outputs(vec![out]).unwrap();
2399 let func_v = builder.load_func(func_id.handle(), &[]).unwrap();
2400 let scan = BArrayScan::new(int_ty.clone(), int_ty.clone(), vec![], size);
2401 let mut arr = builder
2402 .add_dataflow_op(scan, [arr, func_v])
2403 .unwrap()
2404 .out_wire(0);
2405
2406 for i in 0..size {
2407 let array_size = size - i;
2408 let pop_res = builder
2409 .add_borrow_array_pop_left(int_ty.clone(), array_size, arr)
2410 .unwrap();
2411 let [elem, new_arr] = builder
2412 .build_unwrap_sum(
2413 1,
2414 option_type(vec![
2415 int_ty.clone(),
2416 borrow_array_type(array_size - 1, int_ty.clone()),
2417 ]),
2418 pop_res,
2419 )
2420 .unwrap();
2421 arr = new_arr;
2422 r = builder.add_iadd(6, r, elem).unwrap();
2423 }
2424 builder
2425 .add_borrow_array_discard_empty(int_ty.clone(), arr)
2426 .unwrap();
2427 builder.finish_hugr_with_outputs([r]).unwrap()
2428 });
2429 exec_ctx.add_extensions(|cge| {
2430 cge.add_default_prelude_extensions()
2431 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2432 .add_default_int_extensions()
2433 });
2434 let expected: u64 = (inc..size + inc).sum();
2435 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2436 }
2437
2438 #[rstest]
2439 #[case(0)]
2440 #[case(1)]
2441 #[case(10)]
2442 fn exec_scan_fold(mut exec_ctx: TestContext, #[case] size: u64) {
2443 let int_ty = int_type(6);
2448 let hugr = SimpleHugrConfig::new()
2449 .with_outs(int_ty.clone())
2450 .with_extensions(exec_registry())
2451 .finish(|mut builder| {
2452 let new_array_args = (0..size)
2453 .map(|i| builder.add_load_value(ConstInt::new_u(6, i).unwrap()))
2454 .collect_vec();
2455 let arr = builder
2456 .add_new_borrow_array(int_ty.clone(), new_array_args)
2457 .unwrap();
2458
2459 let mut mb = builder.module_root_builder();
2460 let mut func = mb
2461 .define_function(
2462 "foo",
2463 Signature::new(
2464 vec![int_ty.clone(), int_ty.clone()],
2465 vec![Type::UNIT, int_ty.clone()],
2466 ),
2467 )
2468 .unwrap();
2469 let [elem, acc] = func.input_wires_arr();
2470 let acc = func.add_iadd(6, elem, acc).unwrap();
2471 let unit = func
2472 .add_dataflow_op(Tag::new(0, vec![type_row![]]), [])
2473 .unwrap()
2474 .out_wire(0);
2475 let func_id = func.finish_with_outputs(vec![unit, acc]).unwrap();
2476 let func_v = builder.load_func(func_id.handle(), &[]).unwrap();
2477 let scan = BArrayScan::new(int_ty.clone(), Type::UNIT, vec![int_ty.clone()], size);
2478 let zero = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2479 let [arr, sum] = builder
2480 .add_dataflow_op(scan, [arr, func_v, zero])
2481 .unwrap()
2482 .outputs_arr();
2483 builder
2484 .add_borrow_array_discard(Type::UNIT, size, arr)
2485 .unwrap();
2486 builder.finish_hugr_with_outputs([sum]).unwrap()
2487 });
2488 exec_ctx.add_extensions(|cge| {
2489 cge.add_default_prelude_extensions()
2490 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2491 .add_default_int_extensions()
2492 });
2493 let expected: u64 = (0..size).sum();
2494 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2495 }
2496
2497 fn build_pops(
2498 builder: &mut impl Dataflow,
2499 elem_ty: HugrType,
2500 size: u64,
2501 mut array: Wire,
2502 num_pops: u64,
2503 ) -> Wire {
2504 for i in 0..num_pops {
2505 let res = builder
2506 .add_borrow_array_pop_left(elem_ty.clone(), size - i, array)
2507 .unwrap();
2508 let [_, arr] = builder
2509 .build_unwrap_sum(
2510 1,
2511 option_type(vec![
2512 elem_ty.clone(),
2513 borrow_array_type(size - i - 1, elem_ty.clone()),
2514 ]),
2515 res,
2516 )
2517 .unwrap();
2518 array = arr;
2519 }
2520 array
2521 }
2522
2523 #[rstest]
2524 #[case::single_block(48, 10, &[0, 11, 12, 13, 36, 37])]
2525 #[case::block_boundary1(65, 0, &[63, 64])]
2526 #[case::block_boundary2(65, 10, &[53, 54])]
2527 #[case::block_boundary3(129, 0, &[63, 64, 127, 128])]
2528 fn exec_borrow_return(
2529 mut exec_ctx: TestContext,
2530 #[case] mut size: u64,
2531 #[case] num_pops: u64,
2532 #[case] indices: &[u64],
2533 ) {
2534 let int_ty = int_type(6);
2542 let hugr = SimpleHugrConfig::new()
2543 .with_outs(int_ty.clone())
2544 .with_extensions(exec_registry())
2545 .finish(|mut builder| {
2546 let array = borrow_array::BArrayValue::new(
2547 int_ty.clone(),
2548 (0..size)
2549 .map(|i| ConstInt::new_u(6, i).unwrap().into())
2550 .collect_vec(),
2551 );
2552 let mut array = builder.add_load_value(array);
2553 array = build_pops(&mut builder, int_ty.clone(), size, array, num_pops);
2554 size -= num_pops;
2555 let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2556 for &i in indices {
2557 let i = builder.add_load_value(ConstUsize::new(i));
2558 let (arr, val) = builder
2559 .add_borrow_array_borrow(int_ty.clone(), size, array, i)
2560 .unwrap();
2561 r = builder.add_iadd(6, r, val).unwrap();
2562 array = arr;
2563 }
2564 let zero = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2565 for &i in indices.iter().rev() {
2566 let i = builder.add_load_value(ConstUsize::new(i));
2567 array = builder
2568 .add_borrow_array_return(int_ty.clone(), size, array, i, zero)
2569 .unwrap();
2570 }
2571 for &i in indices {
2572 let i = builder.add_load_value(ConstUsize::new(i));
2573 let (arr, val) = builder
2574 .add_borrow_array_borrow(int_ty.clone(), size, array, i)
2575 .unwrap();
2576 r = builder.add_iadd(6, r, val).unwrap();
2577 array = arr;
2578 }
2579 builder
2580 .add_borrow_array_discard(int_ty.clone(), size, array)
2581 .unwrap();
2582 builder.finish_hugr_with_outputs([r]).unwrap()
2583 });
2584 exec_ctx.add_extensions(|cge| {
2585 cge.add_default_prelude_extensions()
2586 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2587 .add_default_int_extensions()
2588 });
2589 let expected: u64 = indices.iter().map(|i| i + num_pops).sum();
2590 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2591 }
2592
2593 #[rstest]
2594 #[case::empty(0, 0)]
2595 #[case::basic(32, 0)]
2596 #[case::boundary(65, 0)]
2597 #[case::pop1(65, 10)]
2598 #[case::pop2(200, 32)]
2599 fn exec_discard_all_borrowed(
2600 mut exec_ctx: TestContext,
2601 #[case] mut size: u64,
2602 #[case] num_pops: u64,
2603 ) {
2604 let int_ty = int_type(6);
2611 let hugr = SimpleHugrConfig::new()
2612 .with_outs(int_ty.clone())
2613 .with_extensions(exec_registry())
2614 .finish(|mut builder| {
2615 let array = borrow_array::BArrayValue::new(
2616 int_ty.clone(),
2617 (0..size)
2618 .map(|i| ConstInt::new_u(6, i).unwrap().into())
2619 .collect_vec(),
2620 );
2621 let mut array = builder.add_load_value(array);
2622 array = build_pops(&mut builder, int_ty.clone(), size, array, num_pops);
2623 size -= num_pops;
2624 let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2625 for i in 0..size {
2626 let i = builder.add_load_value(ConstUsize::new(i));
2627 let (arr, val) = builder
2628 .add_borrow_array_borrow(int_ty.clone(), size, array, i)
2629 .unwrap();
2630 r = builder.add_iadd(6, r, val).unwrap();
2631 array = arr;
2632 }
2633 builder
2634 .add_discard_all_borrowed(int_ty.clone(), size, array)
2635 .unwrap();
2636 builder.finish_hugr_with_outputs([r]).unwrap()
2637 });
2638 exec_ctx.add_extensions(|cge| {
2639 cge.add_default_prelude_extensions()
2640 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2641 .add_default_int_extensions()
2642 });
2643 let expected: u64 = (num_pops..size + num_pops).sum();
2644 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2645 }
2646
2647 #[rstest]
2648 #[case::small(10, 0)]
2649 #[case::small_pop(10, 2)]
2650 #[case::oneword(64, 0)]
2651 #[case::oneword_pop(64, 5)]
2652 #[case::big(97, 0)]
2653 #[case::big_pop(97, 5)]
2654 #[case::big_pop_until_small(97, 65)]
2655 fn exec_discard_all_borrowed2(
2656 mut exec_ctx: TestContext,
2657 #[case] size: u64,
2658 #[case] num_pops: u64,
2659 ) {
2660 let int_ty = int_type(6);
2661 let hugr = SimpleHugrConfig::new()
2662 .with_extensions(exec_registry())
2663 .finish(|mut builder| {
2664 let mut array = builder.add_new_all_borrowed(int_ty.clone(), size).unwrap();
2665 let val = builder.add_load_value(ConstInt::new_u(6, 15).unwrap());
2666 for i in 0..num_pops {
2667 let i = builder.add_load_value(ConstUsize::new(i));
2668 array = builder
2669 .add_borrow_array_return(int_ty.clone(), size, array, i, val)
2670 .unwrap();
2671 }
2672 let array = build_pops(&mut builder, int_ty.clone(), size, array, num_pops);
2673 builder
2674 .add_discard_all_borrowed(int_ty.clone(), size - num_pops, array)
2675 .unwrap();
2676 builder.finish_hugr_with_outputs([]).unwrap()
2677 });
2678
2679 exec_ctx.add_extensions(|cge| {
2680 cge.add_prelude_extensions(PanicTestPreludeCodegen)
2681 .add_default_borrow_array_extensions(PanicTestPreludeCodegen)
2682 .add_default_int_extensions()
2683 });
2684 assert_eq!(&exec_ctx.exec_hugr_panicking(hugr, "main"), "");
2685 }
2686
2687 #[rstest]
2688 #[case::basic(32, 0)]
2689 #[case::boundary(65, 0)]
2690 #[case::pop1(65, 10)]
2691 #[case::pop2(200, 32)]
2692 fn exec_conversion_roundtrip(
2693 mut exec_ctx: TestContext,
2694 #[case] mut size: u64,
2695 #[case] num_pops: u64,
2696 ) {
2697 let int_ty = int_type(6);
2705 let hugr = SimpleHugrConfig::new()
2706 .with_outs(int_ty.clone())
2707 .with_extensions(exec_registry())
2708 .finish(|mut builder| {
2709 use hugr_core::std_extensions::collections::borrow_array::BArrayFromArray;
2710
2711 let barray = borrow_array::BArrayValue::new(
2712 int_ty.clone(),
2713 (0..size)
2714 .map(|i| ConstInt::new_u(6, i).unwrap().into())
2715 .collect_vec(),
2716 );
2717 let barray = builder.add_load_value(barray);
2718 let barray = build_pops(&mut builder, int_ty.clone(), size, barray, num_pops);
2719 size -= num_pops;
2720 let array = builder
2721 .add_dataflow_op(BArrayToArray::new(int_ty.clone(), size), [barray])
2722 .unwrap()
2723 .out_wire(0);
2724 let mut barray = builder
2725 .add_dataflow_op(BArrayFromArray::new(int_ty.clone(), size), [array])
2726 .unwrap()
2727 .out_wire(0);
2728
2729 let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2730 for i in 0..size {
2731 let i = builder.add_load_value(ConstUsize::new(i));
2732 let (arr, val) = builder
2733 .add_borrow_array_borrow(int_ty.clone(), size, barray, i)
2734 .unwrap();
2735 r = builder.add_iadd(6, r, val).unwrap();
2736 barray = arr;
2737 }
2738 builder
2739 .add_discard_all_borrowed(int_ty.clone(), size, barray)
2740 .unwrap();
2741 builder.finish_hugr_with_outputs([r]).unwrap()
2742 });
2743 exec_ctx.add_extensions(|cge| {
2744 cge.add_default_prelude_extensions()
2745 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
2746 .add_default_array_extensions()
2747 .add_default_int_extensions()
2748 });
2749 let expected: u64 = (num_pops..size + num_pops).sum();
2750 assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main"));
2751 }
2752
2753 #[rstest]
2754 #[case::oob(1, 0, [0, 1], "Index out of bounds")]
2755 #[case::double_borrow(32, 0, [0, 0], "Array element is already borrowed")]
2756 #[case::double_borrow_pop(32, 1, [1, 0], "Array element is already borrowed")]
2757 #[case::double_borrow_boundary(65, 1, [64, 63], "Array element is already borrowed")]
2758 #[case::pop_borrowed(32, 1, [0, 10], "Array element is already borrowed")]
2759 fn exec_borrow_panics(
2760 mut exec_ctx: TestContext,
2761 #[case] mut size: u64,
2762 #[case] num_pops: u64,
2763 #[case] indices: [u64; 2],
2764 #[case] msg: &str,
2765 ) {
2766 let int_ty = int_type(6);
2774 let hugr = SimpleHugrConfig::new()
2775 .with_extensions(exec_registry())
2776 .finish(|mut builder| {
2777 let array = borrow_array::BArrayValue::new(
2778 int_ty.clone(),
2779 (0..size)
2780 .map(|i| ConstInt::new_u(6, i).unwrap().into())
2781 .collect_vec(),
2782 );
2783 let mut array = builder.add_load_value(array);
2784 let i1 = builder.add_load_value(ConstUsize::new(indices[0]));
2785 let i2 = builder.add_load_value(ConstUsize::new(indices[1]));
2786 array = builder
2787 .add_borrow_array_borrow(int_ty.clone(), size, array, i1)
2788 .unwrap()
2789 .0;
2790 array = build_pops(&mut builder, int_ty.clone(), size, array, num_pops);
2791 size -= num_pops;
2792 array = builder
2793 .add_borrow_array_borrow(int_ty.clone(), size, array, i2)
2794 .unwrap()
2795 .0;
2796 builder
2797 .add_borrow_array_discard(int_ty.clone(), size, array)
2798 .unwrap();
2799 builder.finish_hugr_with_outputs([]).unwrap()
2800 });
2801
2802 exec_ctx.add_extensions(|cge| {
2803 cge.add_prelude_extensions(PanicTestPreludeCodegen)
2804 .add_default_borrow_array_extensions(PanicTestPreludeCodegen)
2805 .add_default_int_extensions()
2806 });
2807 assert_eq!(&exec_ctx.exec_hugr_panicking(hugr, "main"), msg);
2808 }
2809
2810 #[rstest]
2811 #[case::oob(1, 0, [0, 1], "Index out of bounds")]
2812 #[case::pop_borrowed(32, 1, [1, 2], "Array element is already borrowed")]
2813 #[case::return_twice(32, 0, [0, 0], "Array already contains an element at this index")]
2814 #[case::return_twice_boundary(65, 0, [64, 64], "Array already contains an element at this index")]
2815 fn exec_return_panics(
2816 mut exec_ctx: TestContext,
2817 #[case] mut size: u64,
2818 #[case] num_pops: u64,
2819 #[case] indices: [u64; 2],
2820 #[case] msg: &str,
2821 ) {
2822 let int_ty = int_type(6);
2830 let hugr = SimpleHugrConfig::new()
2831 .with_extensions(exec_registry())
2832 .finish(|mut builder| {
2833 let mut array = builder.add_new_all_borrowed(int_ty.clone(), size).unwrap();
2834 let i1 = builder.add_load_value(ConstUsize::new(indices[0]));
2835 let i2 = builder.add_load_value(ConstUsize::new(indices[1]));
2836 let zero = builder.add_load_value(ConstInt::new_u(6, 0).unwrap());
2837 array = builder
2838 .add_borrow_array_return(int_ty.clone(), size, array, i1, zero)
2839 .unwrap();
2840 array = build_pops(&mut builder, int_ty.clone(), size, array, num_pops);
2841 size -= num_pops;
2842 array = builder
2843 .add_borrow_array_return(int_ty.clone(), size, array, i2, zero)
2844 .unwrap();
2845 builder
2846 .add_borrow_array_discard(int_ty.clone(), size, array)
2847 .unwrap();
2848 builder.finish_hugr_with_outputs([]).unwrap()
2849 });
2850
2851 exec_ctx.add_extensions(|cge| {
2852 cge.add_prelude_extensions(PanicTestPreludeCodegen)
2853 .add_default_borrow_array_extensions(PanicTestPreludeCodegen)
2854 .add_default_int_extensions()
2855 });
2856 assert_eq!(&exec_ctx.exec_hugr_panicking(hugr, "main"), msg);
2857 }
2858
2859 #[rstest]
2860 fn exec_shift_by_64(mut exec_ctx: TestContext, #[values(false, true)] right: bool) {
2861 const SHIFTED_VAL: u64 = 35;
2862 use hugr_core::std_extensions::arithmetic::int_ops::IntOpDef;
2865 let int_ty = int_type(6); let hugr = SimpleHugrConfig::new()
2867 .with_outs(int_ty.clone())
2868 .with_extensions(exec_registry())
2869 .finish(|mut builder| {
2870 let minus_one = builder.add_load_value(ConstInt::new_u(6, SHIFTED_VAL).unwrap());
2871 let shift = builder.add_load_value(ConstInt::new_u(6, 64).unwrap());
2872 let op = if right {
2873 IntOpDef::ishr
2874 } else {
2875 IntOpDef::ishl
2876 };
2877 let result = builder
2878 .add_dataflow_op(op.with_log_width(6), [minus_one, shift])
2879 .unwrap()
2880 .out_wire(0);
2881 builder.finish_hugr_with_outputs([result]).unwrap()
2882 });
2883 exec_ctx.add_extensions(|cge| {
2884 cge.add_default_prelude_extensions()
2885 .add_default_int_extensions()
2886 });
2887 assert_eq!(exec_ctx.exec_hugr_u64(hugr, "main"), SHIFTED_VAL);
2888 }
2889
2890 #[rstest]
2891 #[case::small(10, 0, 0)]
2892 #[case::small_right(10, 0, 9)]
2893 #[case::small_pop(10, 2, 0)]
2894 #[case::small_right_pop(10, 2, 7)]
2895 #[case::oneword(64, 0, 0)]
2896 #[case::oneword_pop(64, 2, 0)]
2897 #[case::big_right(97, 0, 96)]
2898 #[case::big_pop(97, 5, 0)]
2899 #[case::big_popmany(97, 65, 0)]
2900 fn exec_discard_all_borrowed_panic(
2901 mut exec_ctx: TestContext,
2902 #[case] size: u64,
2903 #[case] num_pops: u64,
2904 #[case] ret_index: u64,
2905 ) {
2906 let int_ty = int_type(6);
2907 let hugr = SimpleHugrConfig::new()
2908 .with_extensions(exec_registry())
2909 .finish(|mut builder| {
2910 let mut array = builder.add_new_all_borrowed(int_ty.clone(), size).unwrap();
2911 let val = builder.add_load_value(ConstInt::new_u(6, 15).unwrap());
2912 for i in 0..num_pops {
2913 let i = builder.add_load_value(ConstUsize::new(i));
2914 array = builder
2915 .add_borrow_array_return(int_ty.clone(), size, array, i, val)
2916 .unwrap();
2917 }
2918 let array = build_pops(&mut builder, int_ty.clone(), size, array, num_pops);
2919 let size = size - num_pops;
2920 let ret_index = builder.add_load_value(ConstUsize::new(ret_index));
2921 let array = builder
2922 .add_borrow_array_return(int_ty.clone(), size, array, ret_index, val)
2923 .unwrap();
2924 builder
2925 .add_discard_all_borrowed(int_ty.clone(), size, array)
2926 .unwrap();
2927 builder.finish_hugr_with_outputs([]).unwrap()
2928 });
2929
2930 exec_ctx.add_extensions(|cge| {
2931 cge.add_prelude_extensions(PanicTestPreludeCodegen)
2932 .add_default_borrow_array_extensions(PanicTestPreludeCodegen)
2933 .add_default_int_extensions()
2934 });
2935 let msg = "Array contains non-borrowed elements and cannot be discarded";
2936 assert_eq!(&exec_ctx.exec_hugr_panicking(hugr, "main"), msg);
2937 }
2938
2939 #[rstest]
2940 fn exec_to_array_panic(mut exec_ctx: TestContext) {
2941 let int_ty = int_type(6);
2942 let size = 10;
2943 let hugr = SimpleHugrConfig::new()
2944 .with_extensions(exec_registry())
2945 .finish(|mut builder| {
2946 let barray = borrow_array::BArrayValue::new(
2947 int_ty.clone(),
2948 (0..size)
2949 .map(|i| ConstInt::new_u(6, i).unwrap().into())
2950 .collect_vec(),
2951 );
2952 let barray = builder.add_load_value(barray);
2953 let idx = builder.add_load_value(ConstUsize::new(0));
2954 let (barray, _) = builder
2955 .add_borrow_array_borrow(int_ty.clone(), size, barray, idx)
2956 .unwrap();
2957 let array = builder
2958 .add_dataflow_op(BArrayToArray::new(int_ty.clone(), size), [barray])
2959 .unwrap()
2960 .out_wire(0);
2961 builder
2962 .add_array_discard(int_ty.clone(), size, array)
2963 .unwrap();
2964 builder.finish_hugr_with_outputs([]).unwrap()
2965 });
2966
2967 exec_ctx.add_extensions(|cge| {
2968 cge.add_prelude_extensions(PanicTestPreludeCodegen)
2969 .add_default_borrow_array_extensions(PanicTestPreludeCodegen)
2970 .add_default_array_extensions()
2971 .add_default_int_extensions()
2972 });
2973 let msg = "Some array elements have been borrowed";
2974 assert_eq!(&exec_ctx.exec_hugr_panicking(hugr, "main"), msg);
2975 }
2976
2977 #[rstest]
2978 fn exec_is_borrowed_basic(mut exec_ctx: TestContext) {
2979 let int_ty = int_type(6);
2985 let size = 3;
2986 let hugr = SimpleHugrConfig::new()
2987 .with_outs(int_ty.clone())
2988 .with_extensions(exec_registry())
2989 .finish(|mut builder| {
2990 let barray = borrow_array::BArrayValue::new(
2991 int_ty.clone(),
2992 (1..=3)
2993 .map(|i| ConstInt::new_u(6, i).unwrap().into())
2994 .collect_vec(),
2995 );
2996 let barray = builder.add_load_value(barray);
2997 let idx1 = builder.add_load_value(ConstUsize::new(1));
2998 let (barray, _) = builder
2999 .add_borrow_array_borrow(int_ty.clone(), size, barray, idx1)
3000 .unwrap();
3001
3002 let idx0 = builder.add_load_value(ConstUsize::new(0));
3003 let (arr, b0_bools) =
3004 [idx0, idx1]
3005 .iter()
3006 .fold((barray, Vec::new()), |(arr, mut bools), idx| {
3007 let (arr, b) = builder
3008 .add_is_borrowed(int_ty.clone(), size, arr, *idx)
3009 .unwrap();
3010 bools.push(b);
3011 (arr, bools)
3012 });
3013 let [b0, b1] = b0_bools.try_into().unwrap();
3014
3015 let b0 = builder.add_not(b0).unwrap(); let and01 = builder.add_and(b0, b1).unwrap();
3017 let i1 = builder
3019 .add_dataflow_op(ConvertOpDef::ifrombool.without_log_width(), [and01])
3020 .unwrap()
3021 .out_wire(0);
3022 let i_64 = builder
3024 .add_dataflow_op(IntOpDef::iwiden_u.with_two_log_widths(0, 6), [i1])
3025 .unwrap()
3026 .out_wire(0);
3027 builder
3028 .add_borrow_array_discard(int_ty.clone(), size, arr)
3029 .unwrap();
3030 builder.finish_hugr_with_outputs([i_64]).unwrap()
3031 });
3032
3033 exec_ctx.add_extensions(|cge| {
3034 cge.add_default_prelude_extensions()
3035 .add_logic_extensions()
3036 .add_conversion_extensions()
3037 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
3038 .add_default_int_extensions()
3039 });
3040 assert_eq!(1, exec_ctx.exec_hugr_u64(hugr, "main"));
3041 }
3042
3043 #[rstest]
3044 fn exec_discard_part_borrowed(mut exec_ctx: TestContext) {
3045 use hugr_passes::replace_types::{DelegatingLinearizer, Linearizer};
3046 let inn_arr_ty = borrow_array_type(2, usize_t());
3052 let arr_ty = borrow_array_type(3, inn_arr_ty.clone());
3053 let hugr = SimpleHugrConfig::new()
3054 .with_outs(int_type(6))
3055 .with_extensions(exec_registry())
3056 .finish(|mut builder| {
3057 let inner_arrays = [1, 3, 5].map(|i| {
3058 let elems = [i, i + 1].map(|v| builder.add_load_value(ConstUsize::new(v)));
3059 builder.add_new_borrow_array(usize_t(), elems).unwrap()
3060 });
3061 let outer = builder
3062 .add_new_borrow_array(inn_arr_ty.clone(), inner_arrays)
3063 .unwrap();
3064 let idx = builder.add_load_value(ConstUsize::new(0));
3065 let (outer, inner) = builder
3066 .add_borrow_array_borrow(inn_arr_ty, 3, outer, idx)
3067 .unwrap();
3068 builder
3069 .add_borrow_array_discard(usize_t(), 2, inner)
3070 .unwrap();
3071 let dl = DelegatingLinearizer::default();
3072 let nt = dl.copy_discard_op(&arr_ty, 0).unwrap();
3073 nt.add(&mut builder, [outer]).unwrap();
3074 let res = builder.add_load_value(ConstInt::new_u(6, 17).unwrap());
3075 builder.finish_hugr_with_outputs([res]).unwrap()
3076 });
3077 exec_ctx.add_extensions(|cge| {
3078 cge.add_default_prelude_extensions()
3079 .add_logic_extensions()
3080 .add_conversion_extensions()
3081 .add_default_borrow_array_extensions(DefaultPreludeCodegen)
3082 .add_default_int_extensions()
3083 });
3084 assert_eq!(17, exec_ctx.exec_hugr_u64(hugr, "main"));
3085 }
3086}