[llvm] [openmp] [OpenMP][OMPT] Error when registering EMI and non-EMI callbacks (PR #213697)

Jan André Reuter via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 08:29:57 PDT 2026


https://github.com/Thyre created https://github.com/llvm/llvm-project/pull/213697

The OpenMP specification includes a restriction for registering both EMI and non-EMI callbacks (e.g. `ompt_callback_target`, OpenMP v5.2, p. 503, l. 18-20):

  These callbacks must not be registered at the same time.

However, this was not respected in the runtime. A tool was able to register both, receiving `ompt_set_always` for each `ompt_set_callback` call. When an event associated with the EMI or non-EMI callbacks was dispatched and both callbacks were registered, only the EMI version was dispatched.

To align the runtime behavior with the OpenMP specification, return `ompt_set_error` when a tool tries to register both the EMI and non-EMI variant for a callback. With this, only the tool's first registration is used.

Update the offload `veccopy_disallow_both.c` test, which already tested the prior, non-compliant, behavior.

>From dbd22185eb20279c40e21ed391f3993cbc917d5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Andr=C3=A9=20Reuter?= <jan at zyten.de>
Date: Mon, 3 Aug 2026 17:29:16 +0200
Subject: [PATCH] [OpenMP][OMPT] Error when registering EMI and non-EMI
 callbacks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The OpenMP specification includes a restriction for registering both EMI and
non-EMI callbacks (e.g. `ompt_callback_target`, OpenMP v5.2, p. 503, l. 18-20):

  These callbacks must not be registered at the same time.

However, this was not respected in the runtime. A tool was able to register
both, receiving `ompt_set_always` for each `ompt_set_callback` call.
When an event associated with the EMI or non-EMI callbacks was dispatched
and both callbacks were registered, only the EMI version was dispatched.

To align the runtime behavior with the OpenMP specification, return
`ompt_set_error` when a tool tries to register both the EMI and non-EMI variant
for a callback. With this, only the tool's first registration is used.

Update the offload `veccopy_disallow_both.c` test, which already tested the
prior, non-compliant, behavior.

Signed-off-by: Jan André Reuter <jan at zyten.de>
---
 offload/test/ompt/register_both.h          |  4 +++-
 offload/test/ompt/veccopy_disallow_both.c  | 19 ++++++++-------
 openmp/runtime/src/include/omp-tools.h.var | 13 +++++++++++
 openmp/runtime/src/ompt-general.cpp        | 27 +++++++++++++++++++++-
 4 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/offload/test/ompt/register_both.h b/offload/test/ompt/register_both.h
index 77cb430aa1f97..4e9157c2b51ed 100644
--- a/offload/test/ompt/register_both.h
+++ b/offload/test/ompt/register_both.h
@@ -4,7 +4,9 @@
 #define register_ompt_callback_t(name, type)                                   \
   do {                                                                         \
     type f_##name = &on_##name;                                                \
-    if (ompt_set_callback(name, (ompt_callback_t)f_##name) == ompt_set_never)  \
+    ompt_set_result_t result =                                                 \
+      ompt_set_callback(name, (ompt_callback_t)f_##name);                      \
+    if (result == ompt_set_never || result == ompt_set_error)                  \
       printf("0: Could not register callback '" #name "'\n");                  \
   } while (0)
 
diff --git a/offload/test/ompt/veccopy_disallow_both.c b/offload/test/ompt/veccopy_disallow_both.c
index bfc67c5f4d274..2e24529042b37 100644
--- a/offload/test/ompt/veccopy_disallow_both.c
+++ b/offload/test/ompt/veccopy_disallow_both.c
@@ -7,8 +7,9 @@
 /*
  * Example OpenMP program that shows that both EMI and non-EMI
  * callbacks cannot be registered for the same type. In the
- * current implementation, the EMI callback overrides the non-EMI
- * callback.
+ * current implementation, the first registered callback takes
+ * precedence. Trying to register the other callback results in
+ * ompt_set_error for this callback.
  */
 
 #include <omp.h>
@@ -57,9 +58,11 @@ int main() {
 }
 
 // clang-format off
+/// CHECK: [[THREAD_ID:[0-9]+]]: Could not register callback 'ompt_callback_target_data_op'
+/// CHECK: [[THREAD_ID]]: Could not register callback 'ompt_callback_target_emi'
 /// CHECK: Callback Init:
 /// CHECK: Callback Load:
-/// CHECK: Callback Target EMI: kind=ompt_target endpoint=ompt_scope_begin
+/// CHECK: Callback Target: target_id=[[TARGET_ID:[0-9]+]] kind=ompt_target endpoint=ompt_scope_begin
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_alloc
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_alloc
 /// CHECK-NOT: dest=(nil)
@@ -70,7 +73,7 @@ int main() {
 /// CHECK-NOT: dest=(nil)
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_transfer_to_device
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_transfer_to_device
-/// CHECK: Callback Submit: target_id=[[TARGET_ID:[0-9]+]] host_op_id=[[HOST_OP_ID:[0-9]+]] req_num_teams=1
+/// CHECK: Callback Submit: target_id=[[TARGET_ID]] host_op_id=[[HOST_OP_ID:[0-9]+]] req_num_teams=1
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_transfer_from_device
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_transfer_from_device
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_transfer_from_device
@@ -79,8 +82,8 @@ int main() {
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_delete
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_delete
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_delete
-/// CHECK: Callback Target EMI: kind=ompt_target endpoint=ompt_scope_end
-/// CHECK: Callback Target EMI: kind=ompt_target endpoint=ompt_scope_begin
+/// CHECK: Callback Target: target_id=[[TARGET_ID]] kind=ompt_target endpoint=ompt_scope_end
+/// CHECK: Callback Target: target_id=[[TARGET_ID:[0-9]+]] kind=ompt_target endpoint=ompt_scope_begin
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_alloc
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_alloc
 /// CHECK-NOT: dest=(nil)
@@ -91,7 +94,7 @@ int main() {
 /// CHECK-NOT: dest=(nil)
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_transfer_to_device
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_transfer_to_device
-/// CHECK: Callback Submit: target_id=[[TARGET_ID:[0-9]+]] host_op_id=[[HOST_OP_ID:[0-9]+]] req_num_teams=0
+/// CHECK: Callback Submit: target_id=[[TARGET_ID]] host_op_id=[[HOST_OP_ID:[0-9]+]] req_num_teams=0
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_transfer_from_device
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_transfer_from_device
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_transfer_from_device
@@ -100,5 +103,5 @@ int main() {
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_delete
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_begin optype=ompt_target_data_delete
 /// CHECK: Callback DataOp EMI: endpoint=ompt_scope_end optype=ompt_target_data_delete
-/// CHECK: Callback Target EMI: kind=ompt_target endpoint=ompt_scope_end
+/// CHECK: Callback Target: target_id=[[TARGET_ID]] kind=ompt_target endpoint=ompt_scope_end
 /// CHECK: Callback Fini:
diff --git a/openmp/runtime/src/include/omp-tools.h.var b/openmp/runtime/src/include/omp-tools.h.var
index af9213a7290e7..46643dff5feab 100644
--- a/openmp/runtime/src/include/omp-tools.h.var
+++ b/openmp/runtime/src/include/omp-tools.h.var
@@ -185,6 +185,19 @@
     /* Optional Events */                                                                                                \
     macro (ompt_callback_target_map_emi,    ompt_callback_target_map_emi_t,    36) /* target map                      */
 
+#define FOREACH_MAPPED_OMPT_TARGET_EVENT(macro)                                                                          \
+    /* macro including correlated callback enum (e.g. target -- target_emi) for comparisons between both */              \
+    /*--- Mandatory Events ---*/                                                                                         \
+    macro (ompt_callback_target,             ompt_callback_target_emi,         ompt_callback_target_t,              8)   \
+    macro (ompt_callback_target_data_op,     ompt_callback_target_data_op_emi, ompt_callback_target_data_op_t,      9)   \
+    macro (ompt_callback_target_submit,      ompt_callback_target_submit_emi,  ompt_callback_target_submit_t,      10)   \
+    macro (ompt_callback_target_emi,         ompt_callback_target,             ompt_callback_target_emi_t,         33)   \
+    macro (ompt_callback_target_data_op_emi, ompt_callback_target_data_op,     ompt_callback_target_data_op_emi_t, 34)   \
+    macro (ompt_callback_target_submit_emi,  ompt_callback_target_submit,      ompt_callback_target_submit_emi_t,  35)   \
+    /* Optional Events */                                                                                                \
+    macro (ompt_callback_target_map,         ompt_callback_target_map_emi,     ompt_callback_target_map_t,         22)   \
+    macro (ompt_callback_target_map_emi,     ompt_callback_target_map,         ompt_callback_target_map_emi_t,     36)
+
 #define FOREACH_OMPT_50_TARGET_EVENT(macro)                                                                              \
     FOREACH_OMPT_DEVICE_EVENT(macro)                                                                                     \
     FOREACH_OMPT_NOEMI_EVENT(macro) 
diff --git a/openmp/runtime/src/ompt-general.cpp b/openmp/runtime/src/ompt-general.cpp
index 1682f7e74bd14..1cb2fb2693a2c 100644
--- a/openmp/runtime/src/ompt-general.cpp
+++ b/openmp/runtime/src/ompt-general.cpp
@@ -597,10 +597,35 @@ OMPT_API_ROUTINE ompt_set_result_t ompt_set_callback(ompt_callbacks_t which,
     else                                                                       \
       return ompt_set_always;
 
-    FOREACH_OMPT_EVENT(ompt_event_macro)
+    FOREACH_OMPT_HOST_EVENT(ompt_event_macro)
+    FOREACH_OMPT_DEVICE_EVENT(ompt_event_macro)
 
 #undef ompt_event_macro
 
+    // OpenMP v5.2, p. 503, l. 18-20:
+    // Restrictions to the ompt_callback_target_emi and ompt_callback_target
+    // callbacks are as follows:
+    //   These callbacks must not be registered at the same time.
+    //
+    // Similar restrictions apply to target_data_op, target_submit,
+    // and target_map. Hence, handle registration of them separately to ensure
+    // only one can be registered at the same time. */
+#define ompt_target_event_macro(event_name, comparison_name, callback_type,    \
+                                event_id)                                      \
+  case event_name:                                                             \
+    if (ompt_callbacks.ompt_callback(comparison_name))                         \
+      return ompt_set_error;                                                   \
+    ompt_callbacks.ompt_callback(event_name) = (callback_type)callback;        \
+    ompt_enabled.event_name = (callback != 0);                                 \
+    if (callback)                                                              \
+      return ompt_event_implementation_status(event_name);                     \
+    else                                                                       \
+      return ompt_set_always;
+
+    FOREACH_MAPPED_OMPT_TARGET_EVENT(ompt_target_event_macro);
+
+#undef ompt_target_event_macro
+
   default:
     return ompt_set_error;
   }



More information about the llvm-commits mailing list