@@ -599,20 +599,16 @@ impl MaskCheck {
599
599
None ,
600
600
|ctx, [ mask_ptr, idx] | {
601
601
// Compute mask bitarray block index via `idx // BLOCK_SIZE`
602
- let mask_ptr = mask_ptr. into_pointer_value ( ) ;
603
- let idx = idx. into_int_value ( ) ;
604
602
let usize_t = usize_ty ( & ctx. typing_session ( ) ) ;
605
- let block_size = usize_t. const_int ( usize_t. get_bit_width ( ) as u64 , false ) ;
606
- let builder = ctx. builder ( ) ;
607
- let block_idx = builder. build_int_unsigned_div ( idx, block_size, "" ) ?;
608
- let block_ptr = unsafe { builder. build_in_bounds_gep ( mask_ptr, & [ block_idx] , "" ) ? } ;
609
- let block = builder. build_load ( block_ptr, "" ) ?. into_int_value ( ) ;
610
-
611
- // Extract bit from the block at position `idx % BLOCK_SIZE`
612
- let idx_in_block = builder. build_int_unsigned_rem ( idx, block_size, "" ) ?;
613
- let block_shifted = builder. build_right_shift ( block, idx_in_block, false , "" ) ?;
614
- let bit =
615
- builder. build_int_truncate ( block_shifted, ctx. iw_context ( ) . bool_type ( ) , "" ) ?;
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
+
616
612
let panic_bb = ctx. build_positioned_new_block ( "panic" , None , |ctx, panic_bb| {
617
613
let err: & ConstError = match self {
618
614
MaskCheck :: CheckNotBorrowed | MaskCheck :: Borrow => & ERR_ALREADY_BORROWED ,
@@ -651,6 +647,38 @@ impl MaskCheck {
651
647
}
652
648
}
653
649
650
+ struct BlockData < ' c > {
651
+ block_ptr : PointerValue < ' c > ,
652
+ block : IntValue < ' c > ,
653
+ idx_in_block : IntValue < ' c > ,
654
+ }
655
+
656
+ fn 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
+
654
682
struct MaskInfo < ' a > {
655
683
mask_ptr : PointerValue < ' a > ,
656
684
offset : IntValue < ' a > ,
@@ -787,6 +815,27 @@ fn build_mask_padding1d<'c, H: HugrView<Node = Node>>(
787
815
Ok ( ( ) )
788
816
}
789
817
818
+ /// Emits a check that returns whether a specific array element is borrowed (true) or not (false).
819
+ pub 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
+ // Wrap the check into a function instead of inlining
825
+ 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
+
790
839
/// Emits a check that no array elements have been borrowed.
791
840
pub fn build_none_borrowed_check < ' c , H : HugrView < Node = Node > > (
792
841
ccg : & impl BorrowArrayCodegen ,
@@ -1570,6 +1619,20 @@ pub fn emit_barray_unsafe_op<'c, H: HugrView<Node = Node>>(
1570
1619
let ( _, array_v) = build_barray_alloc ( ctx, ccg, elem_ty, size, true ) ?;
1571
1620
outputs. finish ( ctx. builder ( ) , [ array_v. into ( ) ] )
1572
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_check(ctx, mask_ptr, offset_index_v)?;
1633
+ let bit = build_is_borrowed_bit ( ctx, mask_ptr, offset_index_v) ?;
1634
+ outputs. finish ( ctx. builder ( ) , [ bit. into ( ) , array_v] )
1635
+ }
1573
1636
_ => todo ! ( ) ,
1574
1637
}
1575
1638
}
@@ -1627,6 +1690,8 @@ mod test {
1627
1690
use hugr_core:: extension:: prelude:: either_type;
1628
1691
use hugr_core:: ops:: Tag ;
1629
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 ;
1630
1695
use hugr_core:: std_extensions:: collections:: array:: ArrayOpBuilder ;
1631
1696
use hugr_core:: std_extensions:: collections:: array:: op_builder:: build_all_borrow_array_ops;
1632
1697
use hugr_core:: std_extensions:: collections:: borrow_array:: {
@@ -2634,7 +2699,7 @@ mod test {
2634
2699
// - Pops specified numbers from the left to introduce an offset
2635
2700
// - Converts it into a regular array
2636
2701
// - Converts it back into a borrow array
2637
- // - Borrows alls elements, sums them up, and returns the sum
2702
+ // - Borrows all elements, sums them up, and returns the sum
2638
2703
2639
2704
let int_ty = int_type ( 6 ) ;
2640
2705
let hugr = SimpleHugrConfig :: new ( )
@@ -2908,4 +2973,70 @@ mod test {
2908
2973
let msg = "Some array elements have been borrowed" ;
2909
2974
assert_eq ! ( & exec_ctx. exec_hugr_panicking( hugr, "main" ) , msg) ;
2910
2975
}
2976
+
2977
+ #[ rstest]
2978
+ fn exec_is_borrowed_basic ( mut exec_ctx : TestContext ) {
2979
+ // We build a HUGR that:
2980
+ // - Creates a borrow array [1,2,3]
2981
+ // - Borrows index 1
2982
+ // - Checks is_borrowed for indices 0, 1
2983
+ // - Returns 1 if [false, true], else 0
2984
+ 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 ( b, arr) = 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 ( ) ; // flip b0 to true
3016
+ let and01 = builder. add_and ( b0, b1) . unwrap ( ) ;
3017
+ // convert bool to i1
3018
+ let i1 = builder
3019
+ . add_dataflow_op ( ConvertOpDef :: ifrombool. without_log_width ( ) , [ and01] )
3020
+ . unwrap ( )
3021
+ . out_wire ( 0 ) ;
3022
+ // widen i1 to i64
3023
+ 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
+ }
2911
3042
}
0 commit comments