! This test checks lowering of OpenACC cache directive. ! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s ! CHECK-LABEL: acc.private.recipe @privatization_ref_i32 : !fir.ref init { ! CHECK-LABEL: func.func @_QPtest_cache_basic() subroutine test_cache_basic() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n !$acc cache(b) a(i) = b(i) end do ! CHECK: acc.loop ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {{{.*}}name = "b" ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_basicEb"} ! Loop body uses the cached reference ! CHECK: %[[ELEM:.*]] = hlfir.designate %[[DECL]]#0 (%{{.*}}) : (!fir.ref>, i64) -> !fir.ref ! CHECK: %[[LOAD:.*]] = fir.load %[[ELEM]] : !fir.ref ! CHECK: hlfir.assign %[[LOAD]] to %{{.*}} : f32, !fir.ref ! Scope termination: acc.yield marks the end of the cache scope ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_readonly() subroutine test_cache_readonly() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n !$acc cache(readonly: b) a(i) = b(i) end do ! CHECK: acc.loop ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {modifiers = #acc, name = "b" ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_readonlyEb"} ! Loop body uses the cached readonly reference ! CHECK: %[[ELEM:.*]] = hlfir.designate %[[DECL]]#0 (%{{.*}}) : (!fir.ref>, i64) -> !fir.ref ! CHECK: %[[LOAD:.*]] = fir.load %[[ELEM]] : !fir.ref ! CHECK: hlfir.assign %[[LOAD]] to %{{.*}} : f32, !fir.ref ! Scope termination ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_array_section() ! For b(2:5) with startIdx=1: lowerbound = 2-1 = 1, upperbound = 5-1 = 4, extent = 4 ! This test includes an IF statement to verify cache scope with unstructured control flow subroutine test_cache_array_section() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n !$acc cache(b(2:5)) if (i > 2) then a(i) = b(i) end if end do ! CHECK: acc.loop ! For b(2:5) with startIdx=1: lowerbound = 2-1 = 1, upperbound = 5-1 = 4 ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! CHECK: %[[LB:.*]] = arith.constant 1 : index ! CHECK: %[[UB:.*]] = arith.constant 4 : index ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%[[LB]] : index) upperbound(%[[UB]] : index) extent(%{{.*}} : index) stride(%[[C1]] : index) startIdx(%[[C1]] : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {{{.*}}name = "b ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_array_sectionEb"} ! Unstructured control flow: IF condition generates fir.if ! CHECK: %[[CMP:.*]] = arith.cmpi sgt, %{{.*}}, %{{.*}} : i32 ! CHECK: fir.if %[[CMP]] { ! Loop body uses the cached array section inside conditional ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.assign ! CHECK: } ! Scope termination: acc.yield terminates the cache scope for all control flow paths ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_multiple() ! This test includes IF-ELSE to verify cache scope with multiple control flow paths subroutine test_cache_multiple() integer, parameter :: n = 10 real, dimension(n) :: a, b, c integer :: i !$acc loop do i = 1, n !$acc cache(b, c) if (i < 5) then a(i) = b(i) + c(i) else a(i) = b(i) - c(i) end if end do ! CHECK: acc.loop ! CHECK: %[[CACHE_B:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {{{.*}}name = "b" ! CHECK: %[[DECL_B:.*]]:2 = hlfir.declare %[[CACHE_B]](%{{.*}}) {uniq_name = "_QFtest_cache_multipleEb"} ! CHECK: %[[CACHE_C:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {{{.*}}name = "c" ! CHECK: %[[DECL_C:.*]]:2 = hlfir.declare %[[CACHE_C]](%{{.*}}) {uniq_name = "_QFtest_cache_multipleEc"} ! Unstructured control flow: IF-ELSE generates fir.if with else region ! CHECK: %[[CMP:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : i32 ! CHECK: fir.if %[[CMP]] { ! Then branch: uses both cached references with addition ! CHECK: hlfir.designate %[[DECL_B]]#0 ! CHECK: fir.load ! CHECK: hlfir.designate %[[DECL_C]]#0 ! CHECK: fir.load ! CHECK: arith.addf ! CHECK: hlfir.assign ! CHECK: } else { ! Else branch: uses both cached references with subtraction ! CHECK: hlfir.designate %[[DECL_B]]#0 ! CHECK: fir.load ! CHECK: hlfir.designate %[[DECL_C]]#0 ! CHECK: fir.load ! CHECK: arith.subf ! CHECK: hlfir.assign ! CHECK: } ! Scope termination: both IF and ELSE paths use cache, then converge to yield ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_2d_array() ! For b(1:5, 1:5) with startIdx=1: each dimension has lowerbound = 1-1 = 0, upperbound = 5-1 = 4, extent = 5 subroutine test_cache_2d_array() integer, parameter :: n = 10 real, dimension(n, n) :: a, b integer :: i, j !$acc loop do i = 1, n !$acc cache(b(1:5, 1:5)) do j = 1, n a(i,j) = b(i,j) end do end do ! CHECK: acc.loop ! Dimension 1: lowerbound = 1 - startIdx = 0, upperbound = 5 - startIdx = 4 ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! CHECK: %[[C0:.*]] = arith.constant 0 : index ! CHECK: %[[C4:.*]] = arith.constant 4 : index ! CHECK: %[[BOUND1:.*]] = acc.bounds lowerbound(%[[C0]] : index) upperbound(%[[C4]] : index) extent(%{{.*}} : index) stride(%[[C1]] : index) startIdx(%[[C1]] : index) ! Dimension 2: lowerbound = 0, upperbound = 4 ! CHECK: %[[C0_2:.*]] = arith.constant 0 : index ! CHECK: %[[C4_2:.*]] = arith.constant 4 : index ! CHECK: %[[BOUND2:.*]] = acc.bounds lowerbound(%[[C0_2]] : index) upperbound(%[[C4_2]] : index) extent(%{{.*}} : index) stride(%[[C1]] : index) startIdx(%[[C1]] : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) bounds(%[[BOUND1]], %[[BOUND2]]) -> !fir.ref> {{{.*}}name = "b ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_2d_arrayEb"} ! Nested loop uses the cached 2D array ! CHECK: fir.do_loop ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.assign ! Scope termination for acc.loop ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_loop_var() ! Test cache with loop variable dependent bounds: b(i:i+2) subroutine test_cache_loop_var() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n-2 !$acc cache(b(i:i+2)) a(i) = b(i) + b(i+1) + b(i+2) end do ! CHECK: acc.loop private({{.*}}) control(%[[IV:.*]] : i32) = ({{.*}}) to ({{.*}}) ! The privatized iterator is declared and initialized from the loop control variable ! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_cache_loop_varEi"} ! CHECK: fir.store %[[IV]] to %[[I_DECL]]#0 : !fir.ref ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! Load iterator i for lowerbound computation ! CHECK: %[[I_LOAD1:.*]] = fir.load %[[I_DECL]]#0 : !fir.ref ! CHECK: %[[I_CVT1:.*]] = fir.convert %[[I_LOAD1]] : (i32) -> i64 ! CHECK: %[[I_IDX1:.*]] = fir.convert %[[I_CVT1]] : (i64) -> index ! Compute lowerbound = i - 1 ! CHECK: %[[LB:.*]] = arith.subi %[[I_IDX1]], %[[C1]] : index ! Load iterator i again for upperbound computation (i+2) ! CHECK: %[[I_LOAD2:.*]] = fir.load %[[I_DECL]]#0 : !fir.ref ! CHECK: %[[C2:.*]] = arith.constant 2 : i32 ! CHECK: %[[I_PLUS_2:.*]] = arith.addi %[[I_LOAD2]], %[[C2]] : i32 ! CHECK: %[[UB_CVT:.*]] = fir.convert %[[I_PLUS_2]] : (i32) -> i64 ! CHECK: %[[UB_IDX:.*]] = fir.convert %[[UB_CVT]] : (i64) -> index ! Compute upperbound = (i+2) - 1 ! CHECK: %[[UB:.*]] = arith.subi %[[UB_IDX]], %[[C1]] : index ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%[[LB]] : index) upperbound(%[[UB]] : index) extent(%{{.*}} : index) stride(%[[C1]] : index) startIdx(%[[C1]] : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {{{.*}}name = "b ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_loop_varEb"} ! Loop body uses the cached reference for b(i), b(i+1), b(i+2) ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: arith.addf ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: arith.addf ! CHECK: hlfir.assign ! Scope termination ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_2d_loop_vars() ! Test 2D cache with swapped loop variables inside nested loop: b(j:j+1, i:i+1) subroutine test_cache_2d_loop_vars() integer, parameter :: n = 10 real, dimension(n, n) :: a, b integer :: i, j !$acc loop do i = 1, n-1 do j = 1, n-1 !$acc cache(b(j:j+1, i:i+1)) a(i,j) = b(j,i) + b(j+1,i+1) end do end do ! CHECK: acc.loop private({{.*}}) control(%[[I_IV:.*]] : i32) = ({{.*}}) to ({{.*}}) ! Outer loop iterator i is stored to privatized variable ! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_cache_2d_loop_varsEi"} ! CHECK: fir.store %[[I_IV]] to %[[I_DECL]]#0 : !fir.ref ! Inner loop j (non-acc loop, fir.do_loop) ! CHECK: fir.do_loop %[[J_IV:.*]] = {{.*}} iter_args(%[[J_ITER:.*]] = {{.*}}) ! Inner loop iterator j is stored to j variable ! CHECK: fir.store %[[J_ITER]] to %[[J_REF:.*]] : !fir.ref ! Dimension 1 bounds from j: lowerbound = j-1, upperbound = j ! CHECK: %[[BOUND1:.*]] = acc.bounds lowerbound(%{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) startIdx(%{{.*}} : index) ! Dimension 2 bounds from i: lowerbound = i-1, upperbound = i ! CHECK: %[[BOUND2:.*]] = acc.bounds lowerbound(%{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) startIdx(%{{.*}} : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) bounds(%[[BOUND1]], %[[BOUND2]]) -> !fir.ref> {{{.*}}name = "b ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_2d_loop_varsEb"} ! Loop body uses the cached 2D reference ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: arith.addf ! CHECK: hlfir.assign ! Inner loop continues within the cache scope ! CHECK: } ! Scope termination for acc.loop ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_single_element() ! Test cache with single element access: b(i) ! This test includes an EXIT statement to verify cache scope with early loop exit subroutine test_cache_single_element() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n !$acc cache(b(i)) a(i) = b(i) if (a(i) > 100.0) exit end do ! Unstructured loop with EXIT: acc.loop becomes unstructured with cf.br/cf.cond_br ! CHECK: acc.loop private({{.*}}) { ! The privatized iterator is declared ! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_cache_single_elementEi"} ! Loop control is done with cf.br/cf.cond_br in unstructured form ! CHECK: cf.br ^[[HEADER:.*]] ! CHECK: ^[[HEADER]]: ! CHECK: cf.cond_br %{{.*}}, ^[[BODY:.*]], ^[[EXIT:.*]] ! CHECK: ^[[BODY]]: ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! Load iterator i for bounds computation ! CHECK: %[[I_LOAD:.*]] = fir.load %[[I_DECL]]#0 : !fir.ref ! CHECK: %[[I_CVT1:.*]] = fir.convert %[[I_LOAD]] : (i32) -> i64 ! CHECK: %[[I_IDX:.*]] = fir.convert %[[I_CVT1]] : (i64) -> index ! Compute lowerbound = i - 1 (single element: upperbound = lowerbound) ! CHECK: %[[LB:.*]] = arith.subi %[[I_IDX]], %[[C1]] : index ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%[[LB]] : index) upperbound(%[[LB]] : index) extent(%[[C1]] : index) stride(%[[C1]] : index) startIdx(%[[C1]] : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {{{.*}}name = "b ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_single_elementEb"} ! Loop body uses the cached single element ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.assign ! Unstructured control flow: EXIT generates conditional branch ! CHECK: %[[CMP:.*]] = arith.cmpf ogt, %{{.*}}, %{{.*}} : f32 ! CHECK: cf.cond_br %[[CMP]], ^[[EXIT_BB:.*]], ^[[CONT_BB:.*]] ! CHECK: ^[[EXIT_BB]]: ! Early exit path: branch to acc.yield ! CHECK: cf.br ^[[YIELD:.*]] ! CHECK: ^[[CONT_BB]]: ! Normal path: update iterator and loop back ! CHECK: cf.br ^[[HEADER]] ! CHECK: ^[[YIELD]]: ! Scope termination: acc.yield marks end of cache scope ! CHECK: acc.yield ! CHECK-NEXT: } attributes {{{.*}}unstructured} end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_mixed_bounds() ! Test cache with mixed constant and variable bounds: b(1:i) ! This test includes a CYCLE statement to verify cache scope with loop continuation subroutine test_cache_mixed_bounds() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n !$acc cache(b(1:i)) if (mod(i, 2) == 0) cycle a(i) = b(i) end do ! CHECK: acc.loop private({{.*}}) control(%[[IV:.*]] : i32) = ({{.*}}) to ({{.*}}) ! The privatized iterator is declared and initialized ! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_cache_mixed_boundsEi"} ! CHECK: fir.store %[[IV]] to %[[I_DECL]]#0 : !fir.ref ! b(1:i): lower bound is constant 0 (1-1), upper bound is i-1 ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! CHECK: %[[C0:.*]] = arith.constant 0 : index ! Load iterator i for upperbound computation ! CHECK: %[[I_LOAD:.*]] = fir.load %[[I_DECL]]#0 : !fir.ref ! CHECK: %[[I_CVT:.*]] = fir.convert %[[I_LOAD]] : (i32) -> i64 ! CHECK: %[[I_IDX:.*]] = fir.convert %[[I_CVT]] : (i64) -> index ! Compute upperbound = i - 1 ! CHECK: %[[UB:.*]] = arith.subi %[[I_IDX]], %[[C1]] : index ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%[[C0]] : index) upperbound(%[[UB]] : index) extent(%{{.*}} : index) stride(%[[C1]] : index) startIdx(%[[C1]] : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}} : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {{{.*}}name = "b ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_mixed_boundsEb"} ! Unstructured control flow: CYCLE generates inverted fir.if (body executes when NOT cycling) ! CHECK: %[[MOD:.*]] = arith.remsi %{{.*}}, %{{.*}} : i32 ! CHECK: %[[CMP:.*]] = arith.cmpi eq, %[[MOD]], %{{.*}} : i32 ! CHECK: %[[TRUE:.*]] = arith.constant true ! CHECK: %[[NOT_CYCLE:.*]] = arith.xori %[[CMP]], %[[TRUE]] : i1 ! CHECK: fir.if %[[NOT_CYCLE]] { ! Loop body uses the cached reference (only executed when not cycling) ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.assign ! CHECK: } ! Scope termination: acc.yield after the conditional ! CHECK: acc.yield ! CHECK-NEXT: } end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_nonunit_lb() ! Test cache with array that has non-1 lower bound: arr(10:20), cache(arr(15)) ! This test includes SELECT CASE for multi-way unstructured control flow subroutine test_cache_nonunit_lb() integer :: arr(10:20) integer :: i !$acc loop do i = 10, 20 !$acc cache(arr(15)) select case (mod(i, 3)) case (0) arr(i) = i * 2 case (1) arr(i) = i * 3 case default arr(i) = i end select end do ! For arr(10:20), startIdx = 10, element 15 has lowerbound = 15 - 10 = 5 ! CHECK: %[[C10:.*]] = arith.constant 10 : index ! Unstructured loop with SELECT CASE: acc.loop becomes unstructured ! CHECK: acc.loop private({{.*}}) { ! CHECK: cf.br ^[[HEADER:.*]] ! CHECK: ^[[HEADER]]: ! CHECK: cf.cond_br %{{.*}}, ^[[BODY:.*]], ^[[EXIT:.*]] ! CHECK: ^[[BODY]]: ! Compute lowerbound = 15 - startIdx = 15 - 10 = 5 ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! CHECK: %[[C15:.*]] = arith.constant 15 : index ! CHECK: %[[LB:.*]] = arith.subi %[[C15]], %{{.*}} : index ! Single element: upperbound equals lowerbound, startIdx = 10 ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%[[LB]] : index) upperbound(%[[LB]] : index) extent(%[[C1]] : index) stride(%{{.*}} : index) startIdx(%{{.*}} : index) {strideInBytes = true} ! For non-unit lower bound arrays, acc.cache uses the box type from hlfir.declare ! CHECK: %[[CACHE:.*]] = acc.cache var(%{{.*}} : !fir.box>) bounds(%[[BOUND]]) -> !fir.box> {{{.*}}name = "arr ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]](%{{.*}}) {uniq_name = "_QFtest_cache_nonunit_lbEarr"} ! Unstructured control flow: SELECT CASE generates fir.select_case ! CHECK: %[[MOD:.*]] = arith.remsi %{{.*}}, %{{.*}} : i32 ! CHECK: fir.select_case %[[MOD]] : i32 [#fir.point, %{{.*}}, ^[[CASE0:.*]], #fir.point, %{{.*}}, ^[[CASE1:.*]], unit, ^[[DEFAULT:.*]]] ! Case 0: i * 2 ! CHECK: ^[[CASE0]]: ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: hlfir.assign ! CHECK: cf.br ^[[MERGE:.*]] ! Case 1: i * 3 ! CHECK: ^[[CASE1]]: ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: hlfir.assign ! CHECK: cf.br ^[[MERGE]] ! Default case: i ! CHECK: ^[[DEFAULT]]: ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: hlfir.assign ! CHECK: cf.br ^[[MERGE]] ! All SELECT CASE branches converge, then loop back or exit ! CHECK: ^[[MERGE]]: ! CHECK: cf.br ^[[HEADER]] ! CHECK: ^[[EXIT]]: ! Scope termination: acc.yield marks end of cache scope ! CHECK: acc.yield ! CHECK-NEXT: } attributes {{{.*}}unstructured} end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_use_after_region() ! CHECK: %[[B_VAR:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_cache_use_after_regionEb"} subroutine test_cache_use_after_region() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i !$acc loop do i = 1, n !$acc cache(b) a(i) = b(i) end do ! Use b after the cache region - should use original variable a(1) = b(1) + 1.0 ! CHECK: acc.loop ! CHECK: acc.cache varPtr(%[[B_VAR]]#0 : !fir.ref>) ! CHECK: acc.yield ! CHECK: } ! After loop: uses original b, not cached version ! CHECK: %[[B_ORIG:.*]] = hlfir.designate %[[B_VAR]]#0 (%{{.*}}) ! CHECK: fir.load %[[B_ORIG]] ! CHECK: arith.addf ! CHECK: hlfir.assign end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_nested_scope() ! CHECK: %[[B_VAR:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_cache_nested_scopeEb"} subroutine test_cache_nested_scope() integer, parameter :: n = 10 real, dimension(n) :: a, b, c integer :: i, j !$acc loop do i = 1, n !$acc loop do j = 1, n !$acc cache(b(j)) c(j) = b(j) end do ! Use b(i) in outer loop - should use original, not inner cache a(i) = b(i) end do ! Outer acc.loop ! CHECK: acc.loop ! Inner acc.loop with cache ! CHECK: acc.loop ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%[[B_VAR]]#0 : !fir.ref>) bounds(%{{.*}}) ! CHECK: %[[CACHE_DECL:.*]]:2 = hlfir.declare %[[CACHE]] ! CHECK: hlfir.designate %[[CACHE_DECL]]#0 ! CHECK: acc.yield ! CHECK: } ! After inner loop: uses original b, not cached ! CHECK: hlfir.designate %[[B_VAR]]#0 ! CHECK: acc.yield end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_in_regular_loop() ! CHECK: %[[B_VAR:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_cache_in_regular_loopEb"} subroutine test_cache_in_regular_loop() integer, parameter :: n = 10 real, dimension(n) :: a, b integer :: i ! Cache in regular DO loop (not acc loop) do i = 1, n !$acc cache(b(i)) a(i) = b(i) end do ! CHECK: fir.do_loop ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%[[B_VAR]]#0 : !fir.ref>) bounds(%{{.*}}) ! CHECK: %[[CACHE_DECL:.*]]:2 = hlfir.declare %[[CACHE]] ! CHECK: hlfir.designate %[[CACHE_DECL]]#0 ! CHECK: fir.load ! CHECK: hlfir.assign end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_in_if ! CHECK: %[[B_VAR:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFtest_cache_in_ifEb"} subroutine test_cache_in_if(a, b, cache) integer, parameter :: n = 10 real, dimension(n) :: a, b logical :: cache integer :: i !$acc loop do i = 1, n if (cache) then !$acc cache(b) end if a(i) = b(i) end do ! CHECK: acc.loop ! CHECK: fir.if ! CHECK: acc.cache varPtr(%[[B_VAR]]#0 : !fir.ref>) ! CHECK: } ! After IF: uses original b, not cached version ! CHECK: hlfir.designate %[[B_VAR]]#0 ! CHECK: acc.yield end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_in_nested_do ! CHECK: %[[B_VAR:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_cache_in_nested_doEb"} subroutine test_cache_in_nested_do() integer, parameter :: n = 1000, m = 100, l = 100 real, dimension(n, m, l) :: a, b integer :: i, j !$acc loop do i = 1, n do j = 1, m, 2 !$acc cache(b(i,m,j)) end do do j = 1, m, 2 a(i, m, :) = b(i, m, :) end do end do ! CHECK: acc.loop ! First inner DO loop with cache ! CHECK: fir.do_loop ! CHECK: acc.cache varPtr(%[[B_VAR]]#0 : !fir.ref>) bounds ! Second inner DO loop: uses original b, not cached version ! CHECK: fir.do_loop ! CHECK: hlfir.designate %[[B_VAR]]#0 end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_derived_type() subroutine test_cache_derived_type() type :: dt real :: array(100) end type integer, parameter :: n = 100 type(dt) :: data real :: a(n) integer :: i !$acc loop do i = 5, n - 4 !$acc cache(data%array(i-4:i+4)) a(i) = data%array(i) end do ! CHECK: acc.loop ! CHECK: %[[ARRAY_COORD:.*]] = hlfir.designate %{{.*}}{"array"} shape %{{.*}} : (!fir.ref}>>, !fir.shape<1>) -> !fir.ref> ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) startIdx(%{{.*}} : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%[[ARRAY_COORD]] : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {name = "data%array(i-4_4:i+4_4)", structured = false} ! CHECK: acc.yield end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_derived_type_readonly() subroutine test_cache_derived_type_readonly() type :: dt real :: array(100) end type integer, parameter :: n = 100 type(dt) :: data real :: a(n) integer :: i !$acc loop do i = 5, n - 4 !$acc cache(readonly: data%array(i-4:i+4)) a(i) = data%array(i) end do ! CHECK: acc.loop ! CHECK: %[[ARRAY_COORD:.*]] = hlfir.designate %{{.*}}{"array"} shape %{{.*}} : (!fir.ref}>>, !fir.shape<1>) -> !fir.ref> ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) startIdx(%{{.*}} : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%[[ARRAY_COORD]] : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {modifiers = #acc, name = "data%array(i-4_4:i+4_4)", structured = false} ! CHECK: acc.yield end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_nested_derived_type() subroutine test_cache_nested_derived_type() type :: inner real :: arr(50) end type type :: outer type(inner) :: in end type integer, parameter :: n = 50 type(outer) :: obj real :: a(n) integer :: i !$acc loop do i = 1, n !$acc cache(obj%in%arr(i)) a(i) = obj%in%arr(i) end do ! CHECK: acc.loop ! CHECK: %[[IN_COORD:.*]] = hlfir.designate %{{.*}}{"in"} : (!fir.ref}>}>>) -> !fir.ref}>> ! CHECK: %[[ARR_COORD:.*]] = hlfir.designate %[[IN_COORD]]{"arr"} shape %{{.*}} : (!fir.ref}>>, !fir.shape<1>) -> !fir.ref> ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound(%{{.*}} : index) upperbound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) startIdx(%{{.*}} : index) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%[[ARR_COORD]] : !fir.ref>) bounds(%[[BOUND]]) -> !fir.ref> {name = "obj%in%arr(i)", structured = false} ! CHECK: acc.yield end subroutine ! Test cache with allocatable component in combined construct ! CHECK-LABEL: func.func @_QPtest_cache_combined_allocatable( subroutine test_cache_combined_allocatable(data, C, M) type :: dt real, dimension(:), allocatable :: A end type type(dt), intent(inout) :: data real, dimension(:), intent(out) :: C integer, intent(in) :: M integer :: i !$acc parallel loop gang vector copyin(data, data%A(-3:M+4)) copyout(C(1:M)) do i = 1, M !$acc cache(data%A(i-4:i+4)) C(i) = data%A(i) end do ! CHECK: acc.parallel {{.*}} { ! CHECK: acc.loop ! CHECK: acc.cache varPtr(%{{.*}}) bounds(%{{.*}}) -> !fir.ref>>> {name = "data%a(i-4_4:i+4_4)", structured = false} ! CHECK: hlfir.declare %{{.*}} {{{.*}}uniq_name = "data%a(i-4_4:i+4_4)"} ! CHECK: acc.yield end subroutine ! Test cache with copy of whole struct (not explicit component copyin) ! CHECK-LABEL: func.func @_QPtest_cache_parallel_copy_struct( subroutine test_cache_parallel_copy_struct(data, M) type :: dt real, dimension(:), allocatable :: A end type type(dt), intent(inout) :: data integer, intent(in) :: M real :: r integer :: i !$acc parallel loop copy(data) do i = 1, M !$acc cache(data%A(i)) r = data%A(i) end do ! CHECK: acc.parallel {{.*}} { ! CHECK: acc.loop ! CHECK: acc.cache varPtr(%{{.*}}) bounds(%{{.*}}) -> !fir.ref>>> {name = "data%a(i)", structured = false} ! CHECK: hlfir.declare %{{.*}} {{{.*}}uniq_name = "data%a(i)"} ! CHECK: acc.yield end subroutine ! Test cache with nested derived type in parallel loop with copyin ! CHECK-LABEL: func.func @_QPtest_cache_nested_parallel( subroutine test_cache_nested_parallel(obj, N) type :: inner real, dimension(:), allocatable :: arr end type type :: outer type(inner) :: in end type type(outer), intent(inout) :: obj integer, intent(in) :: N real :: r integer :: i !$acc parallel loop copyin(obj%in%arr(1:N)) do i = 1, N !$acc cache(obj%in%arr(i)) r = obj%in%arr(i) end do ! CHECK: acc.parallel {{.*}} { ! CHECK: acc.loop ! CHECK: acc.cache varPtr(%{{.*}}) bounds(%{{.*}}) -> !fir.ref>>> {name = "obj%in%arr(i)", structured = false} ! CHECK: hlfir.declare %{{.*}} {{{.*}}uniq_name = "obj%in%arr(i)"} ! CHECK: acc.yield end subroutine ! Test cache with explicit shape component in parallel loop with copyin ! CHECK-LABEL: func.func @_QPtest_cache_explicit_shape_comp( subroutine test_cache_explicit_shape_comp(data, C, M) type :: dt real, dimension(10) :: A end type type(dt), intent(inout) :: data real, dimension(:), intent(out) :: C integer, intent(in) :: M integer :: i !$acc parallel loop gang vector copyin(data, data%A(1:M)) copyout(C(1:M)) do i = 1, M !$acc cache(data%A(i:i+4)) C(i) = data%A(i) end do ! CHECK: acc.parallel {{.*}} { ! CHECK: acc.loop ! CHECK: acc.cache varPtr(%{{.*}}) bounds(%{{.*}}) -> !fir.ref> {name = "data%a(i:i+4_4)", structured = false} ! CHECK: hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "data%a(i:i+4_4)"} ! CHECK: acc.yield end subroutine ! Test cache with temporary in designator bounds - verifies local statement context ! doesn't cause issues with temporary cleanup ! CHECK-LABEL: func.func @_QPtest_cache_temp_in_designator( subroutine test_cache_temp_in_designator(data, a) integer, parameter :: n = 100 real :: data(n) real :: a(n) integer :: i !$acc loop do i = 5, n - 4 !$acc cache(readonly: data(1:maxloc(a+a, dim=1))) a(i) = data(i) end do ! CHECK: acc.loop ! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental ! CHECK: %[[MAXLOC:.*]] = hlfir.maxloc %[[ELEMENTAL]] ! CHECK: %[[BOUND:.*]] = acc.bounds lowerbound({{.*}}) upperbound({{.*}}) ! CHECK: %[[CACHE:.*]] = acc.cache varPtr(%{{.*}}) bounds(%[[BOUND]]) -> !fir.ref> {modifiers = #acc, name = "data(1:maxloc(a+a,dim=1_4))", structured = false} ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[CACHE]] ! CHECK: hlfir.destroy %[[ELEMENTAL]] ! CHECK: hlfir.designate %[[DECL]]#0 ! CHECK: acc.yield end subroutine subroutine full_array_cache() integer :: k, j , kd, jd,y, x real(8) :: tile(0:8,0:8) !$acc parallel loop gang collapse(2) do k = 1, kd do j = 1, jd !$acc cache(tile(:,:)) end do end do end subroutine ! CHECK-LABEL: func.func @_QPfull_array_cache() ! CHECK: acc.cache var(%{{.*}}) bounds(%{{.*}}) ! Caching a section of an array with a non-unit lower bound produces a ! box-typed acc.cache. The cache descriptor's extents and strides are decided ! later by cache materialization, so the cached symbol must be re-declared with ! the ORIGINAL lower bound ONLY -- a fir.shift, never a fir.shape_shift. A ! fir.shape_shift would re-impose the host array's extents on the descriptor and ! defeat the materialized (possibly packed) layout. subroutine test_cache_nonunit_lb_box() integer :: arr(10:20) integer :: i !$acc loop do i = 10, 20 !$acc cache(arr(12:18)) arr(i) = i end do end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_nonunit_lb_box() ! The host array is declared with its full shape: lower bound 10, extent 11. ! CHECK: %[[LB:.*]] = arith.constant 10 : index ! CHECK: %[[EXT:.*]] = arith.constant 11 : index ! CHECK: %[[SHAPE:.*]] = fir.shape_shift %[[LB]], %[[EXT]] : (index, index) -> !fir.shapeshift<1> ! CHECK: %[[ARR:.*]]:2 = hlfir.declare %{{.*}}(%[[SHAPE]]) {uniq_name = "_QFtest_cache_nonunit_lb_boxEarr"} : (!fir.ref>, !fir.shapeshift<1>) -> (!fir.box>, !fir.ref>) ! CHECK: %[[CACHE:.*]] = acc.cache var(%[[ARR]]#0 : !fir.box>) bounds(%{{.*}}) -> !fir.box> {name = "arr(12:18)" ! The cached re-declaration reuses the SAME lower bound (10) with NO extent: a ! fir.shift on %[[LB]], never a fir.shape_shift. ! CHECK-NOT: fir.shape_shift ! CHECK: %[[SHIFT:.*]] = fir.shift %[[LB]] : (index) -> !fir.shift<1> ! CHECK: hlfir.declare %[[CACHE]](%[[SHIFT]]) {uniq_name = "_QFtest_cache_nonunit_lb_boxEarr"} : (!fir.box>, !fir.shift<1>) -> (!fir.box>, !fir.box>) ! Same property for an assumed-shape array (runtime descriptor): the cached ! re-declaration uses a multi-dimensional fir.shift (lower bounds only), so the ! rebox preserves the descriptor's extents/strides instead of reshaping them. subroutine test_cache_assumed_shape_box(arr) real(8) :: arr(0:, 0:) integer :: i !$acc loop do i = 1, 10 !$acc cache(arr(3:8, 0:4)) arr(i, 0) = arr(i, 1) end do end subroutine ! CHECK-LABEL: func.func @_QPtest_cache_assumed_shape_box( ! Both declared lower bounds are 0 (arr(0:,0:)): ! CHECK: %[[C0A:.*]] = arith.constant 0 : i64 ! CHECK: %[[LB0:.*]] = fir.convert %[[C0A]] : (i64) -> index ! CHECK: %[[C0B:.*]] = arith.constant 0 : i64 ! CHECK: %[[LB1:.*]] = fir.convert %[[C0B]] : (i64) -> index ! CHECK: %[[ARR:.*]]:2 = hlfir.declare %arg0(%{{.*}}) dummy_scope %{{.*}} {{.*}}uniq_name = "_QFtest_cache_assumed_shape_boxEarr"} ! CHECK: %[[CACHE:.*]] = acc.cache var(%[[ARR]]#0 : !fir.box>) bounds(%{{.*}}, %{{.*}}) -> !fir.box> {name = "arr(3:8,0:4)" ! The cached re-declaration reuses the SAME two lower bounds (0, 0) with no ! extents: a fir.shift on %[[LB0]], %[[LB1]], never a fir.shape_shift. ! CHECK-NOT: fir.shape_shift ! CHECK: %[[SHIFT:.*]] = fir.shift %[[LB0]], %[[LB1]] : (index, index) -> !fir.shift<2> ! CHECK: hlfir.declare %[[CACHE]](%[[SHIFT]]) {uniq_name = "_QFtest_cache_assumed_shape_boxEarr"} : (!fir.box>, !fir.shift<2>) -> (!fir.box>, !fir.box>)