[clang] 889708e - [CIR][OpenMP] Enable emission of target functions (#193204)

via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 05:18:38 PDT 2026


Author: Jan Leyonberg
Date: 2026-04-27T08:18:34-04:00
New Revision: 889708eed861b4218fda5a085b47f37b6b73b739

URL: https://github.com/llvm/llvm-project/commit/889708eed861b4218fda5a085b47f37b6b73b739
DIFF: https://github.com/llvm/llvm-project/commit/889708eed861b4218fda5a085b47f37b6b73b739.diff

LOG: [CIR][OpenMP] Enable emission of target functions (#193204)

This PR allows generation of target device functions for OpenMP. It also
handles filtering out host functions that do not contain target regions.

Assisted-by: Cursor / claude-4.6-opus-high

Added: 
    clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp
    clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h
    clang/test/CIR/CodeGenOpenMP/emit-device-functions.cpp

Modified: 
    clang/lib/CIR/CodeGen/CIRGenModule.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.h
    clang/lib/CIR/CodeGen/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 540d35c2a7e0a..f69bd58cd3188 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -24,6 +24,7 @@
 #include "clang/AST/DeclOpenACC.h"
 #include "clang/AST/GlobalDecl.h"
 #include "clang/AST/RecordLayout.h"
+#include "clang/AST/StmtOpenMP.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/CIR/Dialect/IR/CIRAttrs.h"
@@ -150,6 +151,8 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
 
   if (langOpts.CUDA)
     createCUDARuntime();
+  if (langOpts.OpenMP)
+    createOpenMPRuntime();
 
   // Set the module name to be the name of the main file. TranslationUnitDecl
   // often contains invalid source locations and isn't a reliable source for the
@@ -183,6 +186,10 @@ void CIRGenModule::createCUDARuntime() {
   cudaRuntime.reset(createNVCUDARuntime(*this));
 }
 
+void CIRGenModule::createOpenMPRuntime() {
+  openMPRuntime = std::make_unique<CIRGenOpenMPRuntime>(*this);
+}
+
 /// FIXME: this could likely be a common helper and not necessarily related
 /// with codegen.
 /// Return the best known alignment for an unknown pointer to a
@@ -455,12 +462,6 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
     return;
   }
 
-  // TODO(OMP): The logic in this function for the 'rest' of the OpenMP
-  // declarative declarations is complicated and needs to be done on a per-kind
-  // basis, so all of that needs to be added when we implement the individual
-  // global-allowed declarations. See uses of `cir::MissingFeatures::openMP
-  // throughout this function.
-
   const auto *global = cast<ValueDecl>(gd.getDecl());
 
   // If this is CUDA, be selective about which declarations we emit.
@@ -493,6 +494,22 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
       return;
   }
 
+  if (langOpts.OpenMP) {
+    // If this is OpenMP, check if it is legal to emit this global normally.
+    if (openMPRuntime && openMPRuntime->emitTargetGlobal(gd))
+      return;
+    if (auto *drd = dyn_cast<OMPDeclareReductionDecl>(global)) {
+      if (mustBeEmitted(global))
+        emitOMPDeclareReduction(drd);
+      return;
+    }
+    if (auto *dmd = dyn_cast<OMPDeclareMapperDecl>(global)) {
+      if (mustBeEmitted(global))
+        emitOMPDeclareMapper(dmd);
+      return;
+    }
+  }
+
   if (const auto *fd = dyn_cast<FunctionDecl>(global)) {
     // Update deferred annotations with the latest declaration if the function
     // was already used or defined.
@@ -601,6 +618,9 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
 
   if (funcDecl->getAttr<AnnotateAttr>())
     deferredAnnotations[getMangledName(gd)] = funcDecl;
+
+  if (getLangOpts().OpenMP && funcDecl->hasAttr<OMPDeclareTargetDeclAttr>())
+    getOpenMPRuntime().emitDeclareTargetFunction(funcDecl, funcOp);
 }
 
 /// Track functions to be called before main() runs.
@@ -2837,11 +2857,21 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
   const Decl *d = gd.getDecl();
 
   if (const auto *fd = cast_or_null<FunctionDecl>(d)) {
-    // For the device mark the function as one that should be emitted.
-    if (getLangOpts().OpenMPIsTargetDevice && fd->isDefined() && !dontDefer &&
-        !isForDefinition)
-      errorNYI(fd->getSourceRange(),
-               "getOrCreateCIRFunction: OpenMP target function");
+    // For the device, mark the function as one that should be emitted.
+    if (getLangOpts().OpenMPIsTargetDevice && openMPRuntime &&
+        !getOpenMPRuntime().markAsGlobalTarget(gd) && fd->isDefined() &&
+        !dontDefer && !isForDefinition) {
+      if (const FunctionDecl *fdDef = fd->getDefinition()) {
+        GlobalDecl gdDef;
+        if (const auto *cd = dyn_cast<CXXConstructorDecl>(fdDef))
+          gdDef = GlobalDecl(cd, gd.getCtorType());
+        else if (const auto *dd = dyn_cast<CXXDestructorDecl>(fdDef))
+          gdDef = GlobalDecl(dd, gd.getDtorType());
+        else
+          gdDef = GlobalDecl(fdDef);
+        emitGlobal(gdDef);
+      }
+    }
 
     // Any attempts to use a MultiVersion function should result in retrieving
     // the iFunc instead. Name mangling will handle the rest of the changes.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 99a0d225642dc..693d85b4b751f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -16,6 +16,7 @@
 #include "CIRGenBuilder.h"
 #include "CIRGenCUDARuntime.h"
 #include "CIRGenCall.h"
+#include "CIRGenOpenMPRuntime.h"
 #include "CIRGenTypeCache.h"
 #include "CIRGenTypes.h"
 #include "CIRGenVTables.h"
@@ -95,6 +96,9 @@ class CIRGenModule : public CIRGenTypeCache {
   /// Holds the CUDA runtime
   std::unique_ptr<CIRGenCUDARuntime> cudaRuntime;
 
+  /// Holds the OpenMP runtime
+  std::unique_ptr<CIRGenOpenMPRuntime> openMPRuntime;
+
   /// Per-function codegen information. Updated everytime emitCIR is called
   /// for FunctionDecls's.
   CIRGenFunction *curCGF = nullptr;
@@ -130,6 +134,7 @@ class CIRGenModule : public CIRGenTypeCache {
   std::vector<const CXXRecordDecl *> opportunisticVTables;
 
   void createCUDARuntime();
+  void createOpenMPRuntime();
 
   /// A helper for constructAttributeList that handles return attributes.
   void constructFunctionReturnAttributes(const CIRGenFunctionInfo &info,
@@ -741,6 +746,11 @@ class CIRGenModule : public CIRGenTypeCache {
     return *cudaRuntime;
   }
 
+  CIRGenOpenMPRuntime &getOpenMPRuntime() {
+    assert(openMPRuntime != nullptr);
+    return *openMPRuntime;
+  }
+
   mlir::IntegerAttr getSize(CharUnits size) {
     return builder.getSizeFromCharUnits(size);
   }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp
new file mode 100644
index 0000000000000..9a0746e63b6e9
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp
@@ -0,0 +1,194 @@
+//===--- CIRGenOpenMPRuntime.cpp - OpenMP code generation helpers ------=--===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers for OpenMP-specific CIR code generation.
+//
+////===--------------------------------------------------------------------===//
+
+#include "CIRGenOpenMPRuntime.h"
+#include "CIRGenModule.h"
+
+#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
+#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
+#include "clang/AST/OpenMPClause.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+static mlir::omp::DeclareTargetDeviceType
+convertDeviceType(OMPDeclareTargetDeclAttr::DevTypeTy devTy) {
+  switch (devTy) {
+  case OMPDeclareTargetDeclAttr::DT_Host:
+    return mlir::omp::DeclareTargetDeviceType::host;
+  case OMPDeclareTargetDeclAttr::DT_NoHost:
+    return mlir::omp::DeclareTargetDeviceType::nohost;
+  case OMPDeclareTargetDeclAttr::DT_Any:
+    return mlir::omp::DeclareTargetDeviceType::any;
+  }
+  llvm_unreachable("unexpected device type");
+}
+
+static mlir::omp::DeclareTargetCaptureClause
+convertCaptureClause(OMPDeclareTargetDeclAttr::MapTypeTy mapTy) {
+  switch (mapTy) {
+  case OMPDeclareTargetDeclAttr::MT_To:
+    return mlir::omp::DeclareTargetCaptureClause::to;
+  case OMPDeclareTargetDeclAttr::MT_Enter:
+    return mlir::omp::DeclareTargetCaptureClause::enter;
+  case OMPDeclareTargetDeclAttr::MT_Link:
+    return mlir::omp::DeclareTargetCaptureClause::link;
+  case OMPDeclareTargetDeclAttr::MT_Local:
+    return mlir::omp::DeclareTargetCaptureClause::none;
+  }
+  llvm_unreachable("unexpected map type");
+}
+
+/// Returns true if the declaration should be skipped based on its
+/// device_type attribute and the current compilation mode.
+static bool isAssumedToBeNotEmitted(const ValueDecl *vd, bool isDevice) {
+  std::optional<OMPDeclareTargetDeclAttr::DevTypeTy> devTy =
+      OMPDeclareTargetDeclAttr::getDeviceType(vd);
+  if (!devTy)
+    return false;
+  // Do not emit device_type(nohost) functions for the host.
+  if (!isDevice && *devTy == OMPDeclareTargetDeclAttr::DT_NoHost)
+    return true;
+  // Do not emit device_type(host) functions for the device.
+  if (isDevice && *devTy == OMPDeclareTargetDeclAttr::DT_Host)
+    return true;
+  return false;
+}
+
+/// Recursively check whether the statement tree contains any OpenMP target
+/// execution directive (e.g. 'omp target', 'omp target parallel', etc.).
+/// Used to identify host functions that must be emitted on the device because
+/// they contain target regions that will be outlined during MLIR lowering.
+static bool containsTargetRegion(const Stmt *s) {
+  if (!s)
+    return false;
+  if (const auto *e = dyn_cast<OMPExecutableDirective>(s))
+    if (isOpenMPTargetExecutionDirective(e->getDirectiveKind()))
+      return true;
+  for (const Stmt *child : s->children())
+    if (containsTargetRegion(child))
+      return true;
+  return false;
+}
+
+bool CIRGenOpenMPRuntime::emitTargetFunctions(GlobalDecl gd) {
+  bool isDevice = cgm.getLangOpts().OpenMPIsTargetDevice;
+
+  if (!isDevice) {
+    if (const auto *fd = dyn_cast<FunctionDecl>(gd.getDecl()))
+      if (isAssumedToBeNotEmitted(cast<ValueDecl>(fd), isDevice))
+        return true;
+    return false;
+  }
+
+  const auto *vd = cast<ValueDecl>(gd.getDecl());
+
+  if (const auto *fd = dyn_cast<FunctionDecl>(vd))
+    if (isAssumedToBeNotEmitted(cast<ValueDecl>(fd), isDevice))
+      return true;
+
+  // Do not emit function if it is not marked as declare target.
+  if (OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(vd) ||
+      alreadyEmittedTargetDecls.count(vd) != 0)
+    return false;
+
+  // We must also host functions that contain target regions,
+  // because the omp.target ops are nested inside the host function rather than
+  // being outlined early. The containsTargetRegion check handles this.
+  if (const auto *fd = dyn_cast<FunctionDecl>(vd))
+    if (fd->doesThisDeclarationHaveABody() &&
+        containsTargetRegion(fd->getBody()))
+      return false;
+
+  return true;
+}
+
+bool CIRGenOpenMPRuntime::emitTargetGlobalVariable(GlobalDecl gd) {
+  if (isAssumedToBeNotEmitted(cast<ValueDecl>(gd.getDecl()),
+                              cgm.getLangOpts().OpenMPIsTargetDevice))
+    return true;
+
+  if (!cgm.getLangOpts().OpenMPIsTargetDevice)
+    return false;
+
+  // We do not need to scan for target regions since there's not early
+  // outlining like in OGCG, they will be emitted as omp.target ops instead.
+
+  const auto *vd = cast<VarDecl>(gd.getDecl());
+
+  // Do not emit variable if it is not marked as declare target.
+  // OGCG also defers link-clause and USM variables here; we emit errorNYI
+  // for those since they are not yet supported.
+  std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> res =
+      OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(vd);
+  if (!res || *res == OMPDeclareTargetDeclAttr::MT_Link ||
+      ((*res == OMPDeclareTargetDeclAttr::MT_To ||
+        *res == OMPDeclareTargetDeclAttr::MT_Enter) &&
+       false /* NYI: HasRequiresUnifiedSharedMemory */)) {
+    if (res && *res == OMPDeclareTargetDeclAttr::MT_Link)
+      cgm.errorNYI(vd->getSourceRange(),
+                   "declare target global variable with link clause");
+    // OGCG defers these variables for later emission. We skip them for now.
+    return true;
+  }
+  return false;
+}
+
+// Mirrors CGOpenMPRuntime::emitTargetGlobal.
+bool CIRGenOpenMPRuntime::emitTargetGlobal(GlobalDecl gd) {
+  if (isa<FunctionDecl>(gd.getDecl()) ||
+      isa<OMPDeclareReductionDecl>(gd.getDecl()))
+    return emitTargetFunctions(gd);
+
+  return emitTargetGlobalVariable(gd);
+}
+
+bool CIRGenOpenMPRuntime::markAsGlobalTarget(GlobalDecl gd) {
+  if (!cgm.getLangOpts().OpenMPIsTargetDevice)
+    return true;
+
+  const auto *d = cast<FunctionDecl>(gd.getDecl());
+
+  if (OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(d)) {
+    if (d->hasBody() && alreadyEmittedTargetDecls.count(d) == 0) {
+      auto f = dyn_cast_if_present<cir::FuncOp>(
+          cgm.getGlobalValue(cgm.getMangledName(gd)));
+      if (f)
+        return !f.isDeclaration();
+      return false;
+    }
+    return true;
+  }
+
+  return !alreadyEmittedTargetDecls.insert(d).second;
+}
+
+void CIRGenOpenMPRuntime::emitDeclareTargetFunction(const FunctionDecl *fd,
+                                                    cir::FuncOp funcOp) {
+  const auto *attr = fd->getAttr<OMPDeclareTargetDeclAttr>();
+  assert(attr && "expected OMPDeclareTargetDeclAttr");
+
+  // Handles the 'indirect' clause here by creating a global variable to hold
+  // the device function address for runtime resolution of indirect calls on
+  // the device.
+  if (std::optional<OMPDeclareTargetDeclAttr *> activeAttr =
+          OMPDeclareTargetDeclAttr::getActiveAttr(fd))
+    if ((*activeAttr)->getIndirect())
+      cgm.errorNYI(fd->getSourceRange(),
+                   "declare target function with indirect clause");
+
+  auto declTargetIface =
+      llvm::cast<mlir::omp::DeclareTargetInterface>(funcOp.getOperation());
+  declTargetIface.setDeclareTarget(convertDeviceType(attr->getDevType()),
+                                   convertCaptureClause(attr->getMapType()),
+                                   /*automap=*/false);
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h
new file mode 100644
index 0000000000000..d22200512e483
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h
@@ -0,0 +1,58 @@
+//===--- CIRGenOpenMPRuntime.h - OpenMP code generation helpers -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENOPENMPRUNTIME_H
+#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENOPENMPRUNTIME_H
+
+#include "clang/AST/DeclOpenMP.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/StmtOpenMP.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "llvm/ADT/DenseSet.h"
+
+namespace clang::CIRGen {
+
+class CIRGenModule;
+
+class CIRGenOpenMPRuntime {
+  CIRGenModule &cgm;
+
+  /// Declarations that have been force-emitted for the target device because
+  /// they are transitively referenced from declare target functions.
+  llvm::DenseSet<CanonicalDeclPtr<const Decl>> alreadyEmittedTargetDecls;
+
+  /// Returns false if the given function or declare reduction should be
+  /// emitted. Returns true if it should eb skipped.
+  /// emission).
+  bool emitTargetFunctions(GlobalDecl gd);
+
+  /// Returns false if given global variable should be emitted. Returns
+  /// true if it should be skipped.
+  bool emitTargetGlobalVariable(GlobalDecl gd);
+
+public:
+  explicit CIRGenOpenMPRuntime(CIRGenModule &cgm) : cgm(cgm) {}
+
+  /// Check whether the given GlobalDecl needs special handling for device
+  /// compilation. Returns false if it should be emitted, true if it should be
+  /// skipped.
+  bool emitTargetGlobal(GlobalDecl gd);
+
+  /// Mark a function reference as one that should be emitted on the device.
+  /// Returns false if it should be emitted, true if the function is already
+  /// handled and should be skipped.
+  bool markAsGlobalTarget(GlobalDecl gd);
+
+  /// If the function has an OMPDeclareTargetDeclAttr, set the corresponding
+  /// omp.declare_target attribute on the emitted cir.func op.
+  void emitDeclareTargetFunction(const FunctionDecl *fd, cir::FuncOp funcOp);
+};
+
+} // namespace clang::CIRGen
+
+#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENOPENMPRUNTIME_H

diff  --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 3a2616fcd2526..b59addbfd6beb 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -43,6 +43,7 @@ add_clang_library(clangCIR
   CIRGenOpenACCClause.cpp
   CIRGenOpenACCRecipe.cpp
   CIRGenOpenMPClause.cpp
+  CIRGenOpenMPRuntime.cpp
   CIRGenPointerAuth.cpp
   CIRGenRecordLayoutBuilder.cpp
   CIRGenStmt.cpp

diff  --git a/clang/test/CIR/CodeGenOpenMP/emit-device-functions.cpp b/clang/test/CIR/CodeGenOpenMP/emit-device-functions.cpp
new file mode 100644
index 0000000000000..e4502991e4afe
--- /dev/null
+++ b/clang/test/CIR/CodeGenOpenMP/emit-device-functions.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-is-target-device \
+// RUN:   -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-is-target-device \
+// RUN:   -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-is-target-device \
+// RUN:   -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// Test device-side filtering of globals, mirroring CGOpenMPRuntime logic:
+// - declare target functions and their transitive callees are emitted
+// - host-only functions are filtered out
+// - device_type(host) functions are filtered out on the device
+// - host functions containing target regions are kept
+
+// ---- declare target block: emitted on device ----
+
+#pragma omp declare target
+
+void regular_func() {}
+
+struct S {
+  int x;
+  S() : x(42) {}
+  ~S() {}
+};
+
+void caller() {
+  regular_func();
+  S s;
+}
+
+#pragma omp end declare target
+
+// CIR-DAG: cir.func {{.*}} @_Z12regular_funcv() {{.*}}omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to)
+// CIR-DAG: cir.func {{.*}} @_Z6callerv() {{.*}}omp.declare_target
+// CIR-DAG: cir.func {{.*}} @_ZN1SC2Ev({{.*}})
+// CIR-DAG: cir.func {{.*}} @_ZN1SC1Ev({{.*}})
+// CIR-DAG: cir.func {{.*}} @_ZN1SD2Ev({{.*}})
+// CIR-DAG: cir.func {{.*}} @_ZN1SD1Ev({{.*}})
+
+// LLVM-DAG: define {{.*}} void @_Z12regular_funcv()
+// LLVM-DAG: define {{.*}} void @_Z6callerv()
+// LLVM-DAG: define {{.*}} void @_ZN1SC2Ev(
+// LLVM-DAG: define {{.*}} void @_ZN1SC1Ev(
+// LLVM-DAG: define {{.*}} void @_ZN1SD2Ev(
+// LLVM-DAG: define {{.*}} void @_ZN1SD1Ev(
+
+// OGCG-DAG: define {{.*}} void @_Z12regular_funcv()
+// OGCG-DAG: define {{.*}} void @_Z6callerv()
+// OGCG-DAG: define {{.*}} void @_ZN1SC2Ev(
+// OGCG-DAG: define {{.*}} void @_ZN1SC1Ev(
+// OGCG-DAG: define {{.*}} void @_ZN1SD2Ev(
+// OGCG-DAG: define {{.*}} void @_ZN1SD1Ev(
+
+// ---- host-only function: NOT emitted on device ----
+
+void host_only_func() {}
+
+// CIR-NOT: @_Z14host_only_funcv
+// LLVM-NOT: @_Z14host_only_funcv
+// OGCG-NOT: @_Z14host_only_funcv
+
+// ---- device_type(host): filtered out on device by isAssumedToBeNotEmitted ----
+
+void host_device_type_func() {}
+#pragma omp declare target to(host_device_type_func) device_type(host)
+
+// CIR-NOT: @_Z20host_device_type_funcv
+// LLVM-NOT: @_Z20host_device_type_funcv
+// OGCG-NOT: @_Z20host_device_type_funcv
+
+// ---- transitive callee: emitted because called from declare target ----
+
+void transitive_callee() {}
+
+#pragma omp declare target
+void calls_transitive() { transitive_callee(); }
+#pragma omp end declare target
+
+// CIR-DAG: cir.func {{.*}} @_Z17transitive_calleev()
+// CIR-DAG: cir.func {{.*}} @_Z16calls_transitivev() {{.*}}omp.declare_target
+
+// LLVM-DAG: define {{.*}} void @_Z17transitive_calleev()
+// LLVM-DAG: define {{.*}} void @_Z16calls_transitivev()
+
+// OGCG-DAG: define {{.*}} void @_Z17transitive_calleev()
+// OGCG-DAG: define {{.*}} void @_Z16calls_transitivev()


        


More information about the cfe-commits mailing list