[OpenMP] Add runtime interface for OpenMP 5.1 error directive

The proposed new interface is for supporting `at(execution)` clause in the
error directive.

Differential Revision: https://reviews.llvm.org/D98448

GitOrigin-RevId: a6f9cb6adc591d19a6c43234245de1e2048ed373
diff --git a/runtime/src/dllexports b/runtime/src/dllexports
index 79bca79..c6be679 100644
--- a/runtime/src/dllexports
+++ b/runtime/src/dllexports
@@ -390,8 +390,9 @@
         __kmpc_taskred_init                 277
         __kmpc_taskred_modifier_init        278
         __kmpc_omp_target_task_alloc        279
+        __kmpc_error                        281
         __kmpc_masked                       282
-        __kmpc_end_masked      	            283
+        __kmpc_end_masked                   283
 %endif
 
 # User API entry points that have both lower- and upper- case versions for Fortran.
diff --git a/runtime/src/i18n/en_US.txt b/runtime/src/i18n/en_US.txt
index dc33fdb..5aa3115 100644
--- a/runtime/src/i18n/en_US.txt
+++ b/runtime/src/i18n/en_US.txt
@@ -455,6 +455,8 @@
 AffUseGlobCpuidL             "%1$s: Affinity capable, using global cpuid leaf %2$d info"
 AffNotCapableUseLocCpuidL    "%1$s: Affinity not capable, using local cpuid leaf %2$d info"
 AffNotUsingHwloc             "%1$s: Affinity not capable, using hwloc."
+UserDirectedError            "%1$s: Encountered user-directed error: %2$s."
+UserDirectedWarning          "%1$s: Encountered user-directed warning: %2$s."
 FailedToCreateTeam           "Failed to create teams between lower bound (%1$d) and upper bound (%2$d)."
 
 # --------------------------------------------------------------------------------------------------
diff --git a/runtime/src/include/omp-tools.h.var b/runtime/src/include/omp-tools.h.var
index 961e767..8e82275 100644
--- a/runtime/src/include/omp-tools.h.var
+++ b/runtime/src/include/omp-tools.h.var
@@ -1099,6 +1099,13 @@
   const void *codeptr_ra
 );
 
+typedef struct ompt_record_error_t {
+  ompt_severity_t severity;
+  const char *message;
+  size_t length;
+  const void *codeptr_ra;
+} ompt_record_error_t;
+
 typedef struct ompd_address_t {
   ompd_seg_t segment;
   ompd_addr_t address;
diff --git a/runtime/src/kmp.h b/runtime/src/kmp.h
index bf4c812..c37e1d9 100644
--- a/runtime/src/kmp.h
+++ b/runtime/src/kmp.h
@@ -4087,6 +4087,13 @@
 #define KMP_GTID_TO_SHADOW_GTID(gtid)                                          \
   ((gtid) % (__kmp_hidden_helper_threads_num - 1) + 2)
 
+// Support for error directive
+typedef enum kmp_severity_t {
+  severity_warning = 1,
+  severity_fatal = 2
+} kmp_severity_t;
+extern void __kmpc_error(ident_t *loc, int severity, const char *message);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/runtime/src/kmp_csupport.cpp b/runtime/src/kmp_csupport.cpp
index 4f34f3a..59d0dec 100644
--- a/runtime/src/kmp_csupport.cpp
+++ b/runtime/src/kmp_csupport.cpp
@@ -4357,3 +4357,35 @@
   }
   return __kmp_pause_resource(level);
 }
+
+void __kmpc_error(ident_t *loc, int severity, const char *message) {
+  if (!__kmp_init_serial)
+    __kmp_serial_initialize();
+
+  KMP_ASSERT(severity == severity_warning || severity == severity_fatal);
+
+#if OMPT_SUPPORT
+  if (ompt_enabled.enabled && ompt_enabled.ompt_callback_error) {
+    ompt_callbacks.ompt_callback(ompt_callback_error)(
+        (ompt_severity_t)severity, message, KMP_STRLEN(message),
+        OMPT_GET_RETURN_ADDRESS(0));
+  }
+#endif // OMPT_SUPPORT
+
+  char *src_loc;
+  if (loc && loc->psource) {
+    kmp_str_loc_t str_loc = __kmp_str_loc_init(loc->psource, false);
+    src_loc =
+        __kmp_str_format("%s:%s:%s", str_loc.file, str_loc.line, str_loc.col);
+    __kmp_str_loc_free(&str_loc);
+  } else {
+    src_loc = __kmp_str_format("unknown");
+  }
+
+  if (severity == severity_warning)
+    KMP_WARNING(UserDirectedWarning, src_loc, message);
+  else
+    KMP_FATAL(UserDirectedError, src_loc, message);
+
+  __kmp_str_free(&src_loc);
+}
diff --git a/runtime/src/ompt-event-specific.h b/runtime/src/ompt-event-specific.h
index 799fa0d..875d692 100644
--- a/runtime/src/ompt-event-specific.h
+++ b/runtime/src/ompt-event-specific.h
@@ -106,6 +106,6 @@
 
 #define ompt_callback_dispatch_implemented ompt_event_UNIMPLEMENTED
 
-#define ompt_callback_error_implemented ompt_event_UNIMPLEMENTED
+#define ompt_callback_error_implemented ompt_event_MAY_ALWAYS_OPTIONAL
 
 #endif
diff --git a/runtime/test/ompt/callback.h b/runtime/test/ompt/callback.h
index e426558..c21b167 100644
--- a/runtime/test/ompt/callback.h
+++ b/runtime/test/ompt/callback.h
@@ -1124,6 +1124,15 @@
   return 0; //success
 }
 
+static void on_ompt_callback_error(ompt_severity_t severity,
+                                   const char *message, size_t length,
+                                   const void *codeptr_ra) {
+  printf("%" PRIu64 ": ompt_event_runtime_error: severity=%" PRIu32
+         ", message=%s, length=%" PRIu64 ", codeptr_ra=%p\n",
+         ompt_get_thread_data()->value, severity, message, (uint64_t)length,
+         codeptr_ra);
+}
+
 int ompt_initialize(
   ompt_function_lookup_t lookup,
   int initial_device_num,
@@ -1173,6 +1182,7 @@
   register_ompt_callback(ompt_callback_task_dependence);
   register_ompt_callback(ompt_callback_thread_begin);
   register_ompt_callback(ompt_callback_thread_end);
+  register_ompt_callback(ompt_callback_error);
   printf("0: NULL_POINTER=%p\n", (void*)NULL);
   return 1; //success
 }
diff --git a/runtime/test/ompt/misc/runtime_error.c b/runtime/test/ompt/misc/runtime_error.c
new file mode 100644
index 0000000..ee9e2e8
--- /dev/null
+++ b/runtime/test/ompt/misc/runtime_error.c
@@ -0,0 +1,35 @@
+// RUN: %libomp-compile-and-run 2>&1 | sort | FileCheck %s
+// REQUIRES: ompt
+
+#include <string.h>
+#include <stdio.h>
+#include "callback.h"
+
+// TODO: use error directive when compiler suppors
+typedef void ident_t;
+extern void __kmpc_error(ident_t *, int, const char *);
+
+int main() {
+#pragma omp parallel num_threads(2)
+  {
+    if (omp_get_thread_num() == 0) {
+      const char *msg = "User message goes here";
+      printf("0: Message length=%" PRIu64 "\n", (uint64_t)strlen(msg));
+      __kmpc_error(NULL, ompt_warning, msg);
+    }
+  }
+  return 0;
+}
+
+// CHECK: {{^}}0: Message length=[[LENGTH:[0-9]+]]
+// CHECK: {{^}}0: NULL_POINTER=[[NULL:.*$]]
+
+// CHECK: {{^}}[[PRIMARY_ID:[0-9]+]]: ompt_event_implicit_task_begin
+// CHECK: {{^}}[[PRIMARY_ID]]: ompt_event_runtime_error
+// CHECK-SAME: severity=1
+// CHECK-SAME: message=User message goes here
+// CHECK-SAME: length=[[LENGTH]]
+// CHECK-SAME: codeptr_ra={{0x[0-f]+}}
+
+// Message from runtime
+// CHECK: {{^}}OMP: Warning{{.*}}User message goes here