[flang][cuda] Register allocator only when needed (#208355)

Register allocator only when needed to avoid bringing in symbol when not
strictly needed.
diff --git a/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp b/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
index 44654ad..47f4acd 100644
--- a/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
+++ b/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
@@ -254,21 +254,51 @@
                           getName() + "pass");
     }
 
-    // Symbol reference to CUFRegisterAllocator.
-    builder.setInsertionPointToEnd(mod.getBody());
-    auto registerFuncOp = mlir::LLVM::LLVMFuncOp::create(
-        builder, loc, RTNAME_STRING(CUFRegisterAllocator), funcTy);
-    registerFuncOp.setVisibility(mlir::SymbolTable::Visibility::Private);
-    auto cufRegisterAllocatorRef = mlir::SymbolRefAttr::get(
-        mod.getContext(), RTNAME_STRING(CUFRegisterAllocator));
-    builder.setInsertionPointToEnd(mod.getBody());
+    bool needAllocatorRegistration = false;
+    mod.walk([&](fir::DeclareOp declOp) {
+      if (declOp.getFortranAttrs() &&
+          fir::bitEnumContainsAny(*declOp.getFortranAttrs(),
+                                  fir::FortranVariableFlagsEnum::allocatable |
+                                      fir::FortranVariableFlagsEnum::pointer)) {
+        needAllocatorRegistration = true;
+        return mlir::WalkResult::interrupt();
+      }
+      return mlir::WalkResult::advance();
+    });
+    if (!needAllocatorRegistration) {
+      mod.walk([&](fir::GlobalOp globalOp) {
+        if (globalOp.getDataAttrAttr()) {
+          if (auto baseBoxType =
+                  mlir::dyn_cast<fir::BaseBoxType>(globalOp.getType())) {
+            if (baseBoxType.isPointerOrAllocatable()) {
+              needAllocatorRegistration = true;
+              return mlir::WalkResult::interrupt();
+            }
+          }
+        }
+        return mlir::WalkResult::advance();
+      });
+    }
 
     // Create the constructor function that call CUFRegisterAllocator.
+    builder.setInsertionPointToEnd(mod.getBody());
     auto func = mlir::LLVM::LLVMFuncOp::create(builder, loc,
                                                cudaFortranCtorName, funcTy);
     func.setLinkage(mlir::LLVM::Linkage::Internal);
-    builder.setInsertionPointToStart(func.addEntryBlock(builder));
-    mlir::LLVM::CallOp::create(builder, loc, funcTy, cufRegisterAllocatorRef);
+    auto entryBlock = func.addEntryBlock(builder);
+    builder.setInsertionPointToStart(entryBlock);
+
+    if (needAllocatorRegistration) {
+      // Symbol reference to CUFRegisterAllocator.
+      builder.setInsertionPointToEnd(mod.getBody());
+      auto registerFuncOp = mlir::LLVM::LLVMFuncOp::create(
+          builder, loc, RTNAME_STRING(CUFRegisterAllocator), funcTy);
+      registerFuncOp.setVisibility(mlir::SymbolTable::Visibility::Private);
+      auto cufRegisterAllocatorRef = mlir::SymbolRefAttr::get(
+          mod.getContext(), RTNAME_STRING(CUFRegisterAllocator));
+      builder.setInsertionPointToStart(entryBlock);
+      mlir::LLVM::CallOp::create(builder, loc, funcTy, cufRegisterAllocatorRef);
+    }
 
     auto gpuMod = symTab.lookup<mlir::gpu::GPUModuleOp>(cudaDeviceModuleName);
     if (gpuMod) {
diff --git a/flang/test/Fir/CUDA/cuda-constructor-2.f90 b/flang/test/Fir/CUDA/cuda-constructor-2.f90
index 26ebcf7..c2a1839 100644
--- a/flang/test/Fir/CUDA/cuda-constructor-2.f90
+++ b/flang/test/Fir/CUDA/cuda-constructor-2.f90
@@ -53,7 +53,7 @@
 
 // Checking that constant global variables are not registered
 
-// CHECK: @_FortranACUFRegisterAllocator
+// CHECK: @__cudaFortranConstructor()
 // CHECK-NOT: fir.call @_FortranACUFRegisterVariable
 
 module attributes {dlti.dl_spec = #dlti.dl_spec<i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i1 = dense<8> : vector<2xi64>, !llvm.ptr = dense<64> : vector<4xi64>, f80 = dense<128> : vector<2xi64>, i128 = dense<128> : vector<2xi64>, i64 = dense<64> : vector<2xi64>, !llvm.ptr<271> = dense<32> : vector<4xi64>, !llvm.ptr<272> = dense<64> : vector<4xi64>, f128 = dense<128> : vector<2xi64>, !llvm.ptr<270> = dense<32> : vector<4xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, "dlti.stack_alignment" = 128 : i64, "dlti.endianness" = "little">, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", gpu.container_module, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.ident = "flang version 20.0.0 (https://github.com/llvm/llvm-project.git 3372303188df0f7f8ac26e7ab610cf8b0f716d42)", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
@@ -174,7 +174,6 @@
 }
 
 // CHECK: llvm.func internal @__cudaFortranConstructor()
-// CHECK-NEXT: llvm.call @_FortranACUFRegisterAllocator()
 // CHECK-NEXT: llvm.return
 // CHECK: llvm.mlir.global_ctors ctors = [@__cudaFortranConstructor]
 
@@ -202,7 +201,7 @@
 }
 
 // CHECK: llvm.func internal @__cudaFortranConstructor()
-// CHECK: llvm.call @_FortranACUFRegisterAllocator()
+// CHECK-NOT: llvm.call @_FortranACUFRegisterAllocator()
 // CHECK: cuf.register_module @cuda_device_mod -> !llvm.ptr 
 // CHECK: fir.address_of(@_QMkernels_mEdev_var) : !fir.ref<f32> 
 // CHECK: fir.call @_FortranACUFRegisterVariable(%3, %4, %5, %6) : (!fir.ref<!fir.llvm_ptr<i8>>, !fir.ref<i8>, !fir.ref<i8>, i64) -> () 
diff --git a/flang/test/Fir/CUDA/cuda-constructor.f90 b/flang/test/Fir/CUDA/cuda-constructor.f90
index 1ba3fdc..e2f2ba8 100644
--- a/flang/test/Fir/CUDA/cuda-constructor.f90
+++ b/flang/test/Fir/CUDA/cuda-constructor.f90
@@ -1,12 +1,12 @@
-! RUN: bbc -fcuda -emit-hlfir %s -o - | fir-opt --cuf-add-constructor | FileCheck %s
+! RUN: bbc -fcuda -emit-fir %s -o - | fir-opt --cuf-add-constructor | FileCheck %s
 
 program main
-  real, device :: ahost(10)
+  real, allocatable, device :: ahost(:)
 end
 
-! CHECK: llvm.func @_FortranACUFRegisterAllocator() attributes {sym_visibility = "private"}
 ! CHECK-LABEL: llvm.func internal @__cudaFortranConstructor() {
 ! CHECK:   llvm.call @_FortranACUFRegisterAllocator() : () -> ()
 ! CHECK:   llvm.return
 ! CHECK: }
+! CHECK: llvm.func @_FortranACUFRegisterAllocator() attributes {sym_visibility = "private"}
 ! CHECK: llvm.mlir.global_ctors ctors = [@__cudaFortranConstructor], priorities = [0 : i32], data = [#llvm.zero]
diff --git a/flang/test/Fir/CUDA/cuda-register-allocator-0.f90 b/flang/test/Fir/CUDA/cuda-register-allocator-0.f90
new file mode 100644
index 0000000..cb40525
--- /dev/null
+++ b/flang/test/Fir/CUDA/cuda-register-allocator-0.f90
@@ -0,0 +1,8 @@
+! RUN: bbc -fcuda -emit-fir %s -o - | fir-opt --cuf-add-constructor | FileCheck %s
+
+module m0
+  integer, device, allocatable :: a(:)
+end module
+
+! CHECK-LABEL: llvm.func internal @__cudaFortranConstructor() {
+! CHECK: llvm.call @_FortranACUFRegisterAllocator()
diff --git a/flang/test/Fir/CUDA/cuda-register-allocator-1.f90 b/flang/test/Fir/CUDA/cuda-register-allocator-1.f90
new file mode 100644
index 0000000..321154c
--- /dev/null
+++ b/flang/test/Fir/CUDA/cuda-register-allocator-1.f90
@@ -0,0 +1,8 @@
+! RUN: bbc -fcuda -emit-fir %s -o - | fir-opt --cuf-add-constructor | FileCheck %s
+
+module m0
+  integer, allocatable :: a(:)
+end module
+
+! CHECK-LABEL: llvm.func internal @__cudaFortranConstructor() {
+! CHECK-NOT: llvm.call @_FortranACUFRegisterAllocator()