# Why is the bounds-checking not elided in isassigned for \`SlowSubArray\`s despite an inbounds annotation?

**URL:** <https://discourse.julialang.org/t/why-is-the-bounds-checking-not-elided-in-isassigned-for-slowsubarray-s-despite-an-inbounds-annotation/110019>\
**Category:** Performance\
**Tags:** array, inbounds, bounds-check\
**Created:** [February 10, 2024, 7:12am UTC](https://discourse.julialang.org/t/why-is-the-bounds-checking-not-elided-in-isassigned-for-slowsubarray-s-despite-an-inbounds-annotation/110019 "2024-02-10T07:12:03Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [February 10, 2024, 7:12am UTC](https://discourse.julialang.org/t/why-is-the-bounds-checking-not-elided-in-isassigned-for-slowsubarray-s-despite-an-inbounds-annotation/110019/1 "2024-02-10T07:12:03Z")

</div>

Function definition

```julia
julia> function mycopyto!(dest::AbstractArray, src::AbstractArray)
           iterdest, itersrc = eachindex(dest), eachindex(src)
           @inbounds for I in iterdest
               if isassigned(src, I) # this shouldn't be checking bounds
                   dest[I] = src[I]
               else
                   dest[I] = zero(eltype(dest))
               end
           end
           return dest
       end
mycopyto! (generic function with 1 method)

julia> @code_llvm mycopyto!(view(zeros(4,4), 1:4, 1:4), view(zeros(4,4), 1:4, 1:4))

```

The relevant bit of the `@code_llvm` output (full output later):

```julia
; @ REPL[1]:4 within `mycopyto!`
; ┌ @ multidimensional.jl:1568 within `isassigned` @ subarray.jl:412
   br label %L36

L36: ; preds = %L590, %L36.preheader
   %value_phi25 = phi i64 [%value_phi276, %L590], [1, %L36.preheader]
   %value_phi26 = phi i64 [%value_phi277, %L590], [1, %L36.preheader]
; │┌ @ abstractarray.jl:681 within `checkbounds`
; ││┌ @ abstractarray.jl:725 within `checkbounds_indices`
; │││┌ @ abstractarray.jl:754 within `checkindex`
; ││││┌ @ int.jl:86 within `-`
       %12 = add i64 %value_phi25, -1
; ││││└
; ││││┌ @ int.jl:513 within `<`
       %13 = icmp uge i64 %12, %7
; │││└└
; │││ @ abstractarray.jl:725 within `checkbounds_indices` @ abstractarray.jl:725
; │││┌ @ abstractarray.jl:754 within `checkindex`
; ││││┌ @ int.jl:86 within `-`
       %14 = add i64 %value_phi26, -1
; ││││└
; ││││┌ @ int.jl:513 within `<`
       %15 = icmp uge i64 %14, %9
; │└└└└
   %.not417.not.not = or i1 %13, %15
; └

```

It appears that the bounds check still exists, despite the inline and inbounds annotations.

> **Full code llvm:**
>
> ```julia
> julia> @code_llvm mycopyto!(view(zeros(4,4),1:4, 1:4), view(zeros(4,4), 1:4, 1:4));
> ; Function Signature: mycopyto!(Base.SubArray{Float64, 2, Array{Float64, 2}, Tuple{Base.UnitRange{Int64}, Base.UnitRange{Int64}}, false}, Base.SubArray{Float64, 2, Array{Float64, 2}, Tuple{Base.UnitRange{Int64}, Base.UnitRange{Int64}}, false})
> ; @ REPL[1]:1 within `mycopyto!`
> define void @"julia_mycopyto!_1916"(ptr noalias nocapture noundef nonnull sret({ ptr, [2 x [2 x i64]], i64, i64 }) align 8 dereferenceable(56) %sret_return, ptr noalias nocapture noundef nonnull align 8 dereferenceable(8) %return_roots, ptr nocapture noundef nonnull readonly align 8 dereferenceable(56) %"dest::SubArray", ptr nocapture noundef nonnull readonly align 8 dereferenceable(56) %"src::SubArray") #0 {
> top:
> ; @ REPL[1]:2 within `mycopyto!`
> ; ┌ @ abstractarray.jl:378 within `eachindex` @ multidimensional.jl:406
> ; │┌ @ subarray.jl:528 within `axes`
> ; ││┌ @ Base.jl:49 within `getproperty`
> %"dest::SubArray.indices_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"dest::SubArray", i64 0, i32 1
> ; ││└
> %"dest::SubArray.indices_ptr[2]_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"dest::SubArray", i64 0, i32 1, i64 1
> ; ││┌ @ subarray.jl:533 within `_indices_sub`
> ; │││┌ @ abstractarray.jl:98 within `axes`
> ; ││││┌ @ range.jl:670 within `size`
> ; │││││┌ @ range.jl:759 within `length`
> ; ││││││┌ @ range.jl:840 within `last`
> ; │││││││┌ @ Base.jl:49 within `getproperty`
> %"dest::SubArray.indices_ptr[1]_ptr.stop_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"dest::SubArray", i64 0, i32 1, i64 0, i64 1
> ; ││││││└└
> ; ││││││ @ range.jl:762 within `length`
> ; ││││││┌ @ int.jl:86 within `-`
> %"dest::SubArray.indices_ptr[1]_ptr.stop_ptr.unbox" = load i64, ptr %"dest::SubArray.indices_ptr[1]_ptr.stop_ptr", align 8
> %"dest::SubArray.indices_ptr[1]_ptr.start_ptr.unbox" = load i64, ptr %"dest::SubArray.indices_ptr", align 8
> %0 = sub i64 %"dest::SubArray.indices_ptr[1]_ptr.stop_ptr.unbox", %"dest::SubArray.indices_ptr[1]_ptr.start_ptr.unbox"
> ; ││││││└
> ; ││││││┌ @ int.jl:87 within `+`
> %1 = add i64 %0, 1
> ; │││└└└└
> ; │││ @ subarray.jl:533 within `_indices_sub` @ subarray.jl:533
> ; │││┌ @ abstractarray.jl:98 within `axes`
> ; ││││┌ @ range.jl:670 within `size`
> ; │││││┌ @ range.jl:759 within `length`
> ; ││││││┌ @ range.jl:840 within `last`
> ; │││││││┌ @ Base.jl:49 within `getproperty`
> %"dest::SubArray.indices_ptr[2]_ptr.stop_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"dest::SubArray", i64 0, i32 1, i64 1, i64 1
> ; ││││││└└
> ; ││││││ @ range.jl:762 within `length`
> ; ││││││┌ @ int.jl:86 within `-`
> %"dest::SubArray.indices_ptr[2]_ptr.stop_ptr.unbox" = load i64, ptr %"dest::SubArray.indices_ptr[2]_ptr.stop_ptr", align 8
> %"dest::SubArray.indices_ptr[2]_ptr.start_ptr.unbox" = load i64, ptr %"dest::SubArray.indices_ptr[2]_ptr", align 8
> %2 = sub i64 %"dest::SubArray.indices_ptr[2]_ptr.stop_ptr.unbox", %"dest::SubArray.indices_ptr[2]_ptr.start_ptr.unbox"
> ; ││││││└
> ; ││││││┌ @ int.jl:87 within `+`
> %3 = add i64 %2, 1
> ; └└└└└└└
> ; @ REPL[1]:3 within `mycopyto!`
> ; ┌ @ multidimensional.jl:416 within `iterate`
> ; │┌ @ tuple.jl:383 within `map`
> ; ││┌ @ range.jl:1420 within `in`
> ; │││┌ @ int.jl:514 within `<=`
> %4 = icmp ugt i64 %0, 9223372036854775806
> %5 = icmp ugt i64 %2, 9223372036854775806
> ; └└└└
> %.not411.not.not.not.not = or i1 %4, %5
> br i1 %.not411.not.not.not.not, label %L623, label %L36.preheader
> 
> L36.preheader: ; preds = %top
> %"src::SubArray.indices_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"src::SubArray", i64 0, i32 1
> %"src::SubArray.indices_ptr[2]_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"src::SubArray", i64 0, i32 1, i64 1
> %"src::SubArray.indices_ptr[1]_ptr.stop_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"src::SubArray", i64 0, i32 1, i64 0, i64 1
> %"src::SubArray.indices_ptr[1]_ptr.stop_ptr.unbox" = load i64, ptr %"src::SubArray.indices_ptr[1]_ptr.stop_ptr", align 8
> %"src::SubArray.indices_ptr[1]_ptr.start_ptr.unbox" = load i64, ptr %"src::SubArray.indices_ptr", align 8
> %6 = add i64 %"src::SubArray.indices_ptr[1]_ptr.stop_ptr.unbox", 1
> %7 = sub i64 %6, %"src::SubArray.indices_ptr[1]_ptr.start_ptr.unbox"
> %"src::SubArray.indices_ptr[2]_ptr.stop_ptr" = getelementptr inbounds { ptr, [2 x [2 x i64]], i64, i64 }, ptr %"src::SubArray", i64 0, i32 1, i64 1, i64 1
> %"src::SubArray.indices_ptr[2]_ptr.stop_ptr.unbox" = load i64, ptr %"src::SubArray.indices_ptr[2]_ptr.stop_ptr", align 8
> %"src::SubArray.indices_ptr[2]_ptr.start_ptr.unbox" = load i64, ptr %"src::SubArray.indices_ptr[2]_ptr", align 8
> %8 = add i64 %"src::SubArray.indices_ptr[2]_ptr.stop_ptr.unbox", 1
> %9 = sub i64 %8, %"src::SubArray.indices_ptr[2]_ptr.start_ptr.unbox"
> %"src::SubArray.parent155" = load atomic ptr, ptr %"src::SubArray" unordered, align 8
> %10 = getelementptr inbounds i8, ptr %"src::SubArray.parent155", i64 16
> %"dest::SubArray.parent" = load atomic ptr, ptr %"dest::SubArray" unordered, align 8
> %11 = getelementptr inbounds i8, ptr %"dest::SubArray.parent", i64 16
> ; @ REPL[1]:4 within `mycopyto!`
> ; ┌ @ multidimensional.jl:1568 within `isassigned` @ subarray.jl:412
> br label %L36
> 
> L36: ; preds = %L590, %L36.preheader
> %value_phi25 = phi i64 [%value_phi276, %L590], [1, %L36.preheader]
> %value_phi26 = phi i64 [%value_phi277, %L590], [1, %L36.preheader]
> ; │┌ @ abstractarray.jl:681 within `checkbounds`
> ; ││┌ @ abstractarray.jl:725 within `checkbounds_indices`
> ; │││┌ @ abstractarray.jl:754 within `checkindex`
> ; ││││┌ @ int.jl:86 within `-`
> %12 = add i64 %value_phi25, -1
> ; ││││└
> ; ││││┌ @ int.jl:513 within `<`
> %13 = icmp uge i64 %12, %7
> ; │││└└
> ; │││ @ abstractarray.jl:725 within `checkbounds_indices` @ abstractarray.jl:725
> ; │││┌ @ abstractarray.jl:754 within `checkindex`
> ; ││││┌ @ int.jl:86 within `-`
> %14 = add i64 %value_phi26, -1
> ; ││││└
> ; ││││┌ @ int.jl:513 within `<`
> %15 = icmp uge i64 %14, %9
> ; │└└└└
> %.not417.not.not = or i1 %13, %15
> ; └
> ; @ REPL[1] within `mycopyto!`
> %16 = add i64 %value_phi25, -2
> ; @ REPL[1]:4 within `mycopyto!`
> br i1 %.not417.not.not, label %L491, label %L211
> 
> L211: ; preds = %L36
> ; @ REPL[1]:5 within `mycopyto!`
> ; ┌ @ abstractarray.jl:1310 within `getindex`
> ; │┌ @ abstractarray.jl:1356 within `_getindex`
> ; ││┌ @ subarray.jl:317 within `getindex` @ array.jl:915
> ; │││┌ @ abstractarray.jl:1345 within `_to_linear_index`
> ; ││││┌ @ abstractarray.jl:2975 within `_sub2ind`
> ; │││││┌ @ abstractarray.jl:98 within `axes`
> ; ││││││┌ @ array.jl:194 within `size`
> %"src::SubArray.parent155.size174.sroa.0.0.copyload" = load i64, ptr %10, align 8
> ; │││└└└└
> ; │││ @ subarray.jl:317 within `getindex`
> ; │││┌ @ subarray.jl:295 within `reindex` @ subarray.jl:295
> ; ││││┌ @ views.jl:150 within `maybeview`
> ; │││││┌ @ array.jl:3058 within `getindex`
> ; ││││││┌ @ range.jl:932 within `_getindex`
> ; │││││││┌ @ int.jl:87 within `+`
> %17 = add i64 %value_phi26, -2
> ; │││└└└└└
> ; │││ @ subarray.jl:317 within `getindex` @ array.jl:915
> ; │││┌ @ abstractarray.jl:1345 within `_to_linear_index`
> ; ││││┌ @ abstractarray.jl:2975 within `_sub2ind` @ abstractarray.jl:2991
> ; │││││┌ @ abstractarray.jl:3007 within `_sub2ind_recurse` @ abstractarray.jl:3007
> ; ││││││┌ @ abstractarray.jl:3014 within `offsetin`
> ; │││││││┌ @ int.jl:86 within `-`
> %18 = add i64 %17, %"src::SubArray.indices_ptr[2]_ptr.start_ptr.unbox"
> ; ││││││└└
> ; ││││││┌ @ int.jl:88 within `*`
> %19 = mul i64 %"src::SubArray.parent155.size174.sroa.0.0.copyload", %18
> ; │││└└└└
> ; │││ @ subarray.jl:317 within `getindex` @ array.jl:915 @ essentials.jl:887
> %20 = load ptr, ptr %"src::SubArray.parent155", align 8
> ; │││ @ subarray.jl:317 within `getindex` @ array.jl:915
> ; │││┌ @ abstractarray.jl:1345 within `_to_linear_index`
> ; ││││┌ @ abstractarray.jl:2975 within `_sub2ind` @ abstractarray.jl:2991
> ; │││││┌ @ abstractarray.jl:3007 within `_sub2ind_recurse` @ abstractarray.jl:3007
> ; ││││││┌ @ int.jl:87 within `+`
> %21 = add i64 %16, %"src::SubArray.indices_ptr[1]_ptr.start_ptr.unbox"
> ; │││└└└└
> ; │││ @ subarray.jl:317 within `getindex` @ array.jl:915 @ essentials.jl:887
> %22 = add i64 %21, %19
> %23 = getelementptr inbounds double, ptr %20, i64 %22
> %24 = load double, ptr %23, align 8
> ; └└└
> ; ┌ @ abstractarray.jl:1411 within `setindex!`
> ; │┌ @ abstractarray.jl:1441 within `_setindex!`
> ; ││┌ @ subarray.jl:368 within `setindex!` @ array.jl:979
> ; │││┌ @ Base.jl:49 within `getproperty`
> %25 = load ptr, ptr %"dest::SubArray.parent", align 8
> ; │││└
> ; │││┌ @ abstractarray.jl:1345 within `_to_linear_index`
> ; ││││┌ @ abstractarray.jl:2975 within `_sub2ind`
> ; │││││┌ @ abstractarray.jl:98 within `axes`
> ; ││││││┌ @ array.jl:194 within `size`
> %"dest::SubArray.parent.size263.sroa.0.0.copyload" = load i64, ptr %11, align 8
> ; │││││└└
> ; │││││ @ abstractarray.jl:2975 within `_sub2ind` @ abstractarray.jl:2991
> ; │││││┌ @ abstractarray.jl:3007 within `_sub2ind_recurse` @ abstractarray.jl:3007
> ; ││││││┌ @ abstractarray.jl:3014 within `offsetin`
> ; │││││││┌ @ int.jl:86 within `-`
> %26 = add i64 %17, %"dest::SubArray.indices_ptr[2]_ptr.start_ptr.unbox"
> ; ││││││└└
> ; ││││││┌ @ int.jl:88 within `*`
> %27 = mul i64 %"dest::SubArray.parent.size263.sroa.0.0.copyload", %26
> ; ││││││└
> ; ││││││┌ @ int.jl:87 within `+`
> %28 = add i64 %16, %"dest::SubArray.indices_ptr[1]_ptr.start_ptr.unbox"
> ; │││└└└└
> %29 = add i64 %28, %27
> %30 = getelementptr inbounds double, ptr %25, i64 %29
> store double %24, ptr %30, align 8
> ; └└└
> br label %L590
> 
> L491: ; preds = %L36
> ; @ REPL[1]:7 within `mycopyto!`
> ; ┌ @ abstractarray.jl:1411 within `setindex!`
> ; │┌ @ abstractarray.jl:1441 within `_setindex!`
> ; ││┌ @ subarray.jl:368 within `setindex!` @ array.jl:979
> ; │││┌ @ Base.jl:49 within `getproperty`
> %31 = load ptr, ptr %"dest::SubArray.parent", align 8
> ; │││└
> ; │││┌ @ abstractarray.jl:1345 within `_to_linear_index`
> ; ││││┌ @ abstractarray.jl:2975 within `_sub2ind`
> ; │││││┌ @ abstractarray.jl:98 within `axes`
> ; ││││││┌ @ array.jl:194 within `size`
> %"dest::SubArray.parent366.size386.sroa.0.0.copyload" = load i64, ptr %11, align 8
> ; │││└└└└
> ; │││ @ subarray.jl:368 within `setindex!`
> ; │││┌ @ subarray.jl:295 within `reindex` @ subarray.jl:295
> ; ││││┌ @ views.jl:150 within `maybeview`
> ; │││││┌ @ array.jl:3058 within `getindex`
> ; ││││││┌ @ range.jl:932 within `_getindex`
> ; │││││││┌ @ int.jl:87 within `+`
> %32 = add i64 %value_phi26, -2
> ; │││└└└└└
> ; │││ @ subarray.jl:368 within `setindex!` @ array.jl:979
> ; │││┌ @ abstractarray.jl:1345 within `_to_linear_index`
> ; ││││┌ @ abstractarray.jl:2975 within `_sub2ind` @ abstractarray.jl:2991
> ; │││││┌ @ abstractarray.jl:3007 within `_sub2ind_recurse` @ abstractarray.jl:3007
> ; ││││││┌ @ abstractarray.jl:3014 within `offsetin`
> ; │││││││┌ @ int.jl:86 within `-`
> %33 = add i64 %32, %"dest::SubArray.indices_ptr[2]_ptr.start_ptr.unbox"
> ; ││││││└└
> ; ││││││┌ @ int.jl:88 within `*`
> %34 = mul i64 %"dest::SubArray.parent366.size386.sroa.0.0.copyload", %33
> ; ││││││└
> ; ││││││┌ @ int.jl:87 within `+`
> %35 = add i64 %16, %"dest::SubArray.indices_ptr[1]_ptr.start_ptr.unbox"
> ; │││└└└└
> %36 = add i64 %35, %34
> %37 = getelementptr inbounds double, ptr %31, i64 %36
> store i64 0, ptr %37, align 8
> ; │└└
> br label %L590
> 
> L590: ; preds = %L491, %L211
> ; └
> ; @ REPL[1]:9 within `mycopyto!`
> ; ┌ @ multidimensional.jl:422 within `iterate`
> ; │┌ @ multidimensional.jl:446 within `__inc`
> ; ││┌ @ int.jl:87 within `+`
> %38 = add i64 %value_phi25, 1
> ; ││└
> ; ││ @ multidimensional.jl:447 within `__inc`
> ; ││┌ @ operators.jl:277 within `!=`
> ; │││┌ @ promotion.jl:620 within `==`
> %.not = icmp eq i64 %value_phi25, %1
> ; ││└└
> %39 = icmp eq i64 %value_phi26, %3
> %narrow.not.not = select i1 %.not, i1 %39, i1 false
> %value_phi276 = select i1 %.not, i64 1, i64 %38
> %40 = zext i1 %.not to i64
> %value_phi277 = add i64 %value_phi26, %40
> ; └└
> br i1 %narrow.not.not, label %L623, label %L36
> 
> L623: ; preds = %L590, %top
> ; @ REPL[1]:10 within `mycopyto!`
> %41 = load ptr, ptr %"dest::SubArray", align 8
> store ptr %41, ptr %return_roots, align 8
> call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(56) %sret_return, ptr noundef nonnull align 8 dereferenceable(56) %"dest::SubArray", i64 56, i1 false)
> ret void
> }
> 
> ```

My version info:

```julia
julia> versioninfo()
Julia Version 1.11.0-DEV.1517
Commit 667cdde7847 (2024-02-08 12:39 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, tigerlake)
Threads: 1 default, 0 interactive, 1 GC (on 8 virtual cores)
Environment:
  LD_LIBRARY_PATH = :/usr/lib/x86_64-linux-gnu/gtk-3.0/modules
  JULIA_EDITOR = subl

```
