[Openmp-commits] [openmp] [OpenMP] Support up to 32 arguments in the generic microtask dispatcher (PR #211071)

via Openmp-commits openmp-commits at lists.llvm.org
Tue Jul 21 10:53:50 PDT 2026


https://github.com/arsnyder16 created https://github.com/llvm/llvm-project/pull/211071

Extend the generic `__kmp_invoke_microtask` implementation to support up to 32 microtask arguments.

The generic dispatcher currently handles at most 15 arguments. OpenMP regions with more captured variables terminate at runtime.

This affects WebAssembly because call_indirect requires the callee type to exactly match the call-site type. Unlike assembly dispatchers on platforms such as x86_64, the generic C++ implementation cannot construct an arbitrary-arity call dynamically and must provide each supported signature explicitly.
Real-world OpenMP regions can exceed the existing limit; the motivating cases require between 16 and 21 arguments.

A longer-term alternative would be to change the compiler/runtime convention for generic-dispatch targets so captured-variable pointers are passed through a single packed context argument. That would remove the fixed upper bound, but it would require coordinated Clang and OpenMP runtime changes and consideration of compatibility with objects produced using the existing variadic microtask ABI. This change keeps the existing ABI and provides a focused runtime-only fix.

>From 7b76acc6c9fa6222b4360dd90ce465df1acaff2a Mon Sep 17 00:00:00 2001
From: Andrew Snyder <asnyder at minitab.com>
Date: Tue, 21 Jul 2026 13:39:00 -0400
Subject: [PATCH] [OpenMP] Support up to 32 arguments in the generic microtask
 dispatcher

---
 openmp/runtime/src/kmp_invoke_microtask.cpp   | 177 +++++++-----------
 .../test/misc_bugs/microtask-32-args.c        |  56 ++++++
 .../test/misc_bugs/wasm-microtask-33-args.c   |  60 ++++++
 3 files changed, 183 insertions(+), 110 deletions(-)
 create mode 100644 openmp/runtime/test/misc_bugs/microtask-32-args.c
 create mode 100644 openmp/runtime/test/misc_bugs/wasm-microtask-33-args.c

diff --git a/openmp/runtime/src/kmp_invoke_microtask.cpp b/openmp/runtime/src/kmp_invoke_microtask.cpp
index f14f27cc06ee3..474423b62188e 100644
--- a/openmp/runtime/src/kmp_invoke_microtask.cpp
+++ b/openmp/runtime/src/kmp_invoke_microtask.cpp
@@ -5,45 +5,33 @@
       KMP_ARCH_ARM || KMP_ARCH_VE || KMP_ARCH_S390X || KMP_ARCH_PPC_XCOFF ||   \
       KMP_ARCH_AARCH64_32)
 
-// Because WebAssembly will use `call_indirect` to invoke the microtask and
-// WebAssembly indirect calls check that the called signature is a precise
-// match, we need to cast each microtask function pointer back from `void *` to
-// its original type.
-typedef void (*microtask_t0)(int *, int *);
-typedef void (*microtask_t1)(int *, int *, void *);
-typedef void (*microtask_t2)(int *, int *, void *, void *);
-typedef void (*microtask_t3)(int *, int *, void *, void *, void *);
-typedef void (*microtask_t4)(int *, int *, void *, void *, void *, void *);
-typedef void (*microtask_t5)(int *, int *, void *, void *, void *, void *,
-                             void *);
-typedef void (*microtask_t6)(int *, int *, void *, void *, void *, void *,
-                             void *, void *);
-typedef void (*microtask_t7)(int *, int *, void *, void *, void *, void *,
-                             void *, void *, void *);
-typedef void (*microtask_t8)(int *, int *, void *, void *, void *, void *,
-                             void *, void *, void *, void *);
-typedef void (*microtask_t9)(int *, int *, void *, void *, void *, void *,
-                             void *, void *, void *, void *, void *);
-typedef void (*microtask_t10)(int *, int *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *, void *);
-typedef void (*microtask_t11)(int *, int *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *, void *,
-                              void *);
-typedef void (*microtask_t12)(int *, int *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *, void *,
-                              void *, void *);
-typedef void (*microtask_t13)(int *, int *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *, void *,
-                              void *, void *, void *);
-typedef void (*microtask_t14)(int *, int *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *, void *,
-                              void *, void *, void *, void *);
-typedef void (*microtask_t15)(int *, int *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *, void *,
-                              void *, void *, void *, void *, void *);
+template <size_t... Indices> struct microtask_index_sequence {};
 
-// we really only need the case with 1 argument, because CLANG always build
-// a struct of pointers to shared variables referenced in the outlined function
+template <size_t N, size_t... Indices>
+struct make_microtask_index_sequence
+    : make_microtask_index_sequence<N - 1, N - 1, Indices...> {};
+
+template <size_t... Indices>
+struct make_microtask_index_sequence<0, Indices...> {
+  using type = microtask_index_sequence<Indices...>;
+};
+
+template <size_t> using microtask_argument_t = void *;
+
+template <size_t... Indices>
+static void invokeMicrotask(microtask_t pkfn, int *gtid, int *tid,
+                            void *p_argv[],
+                            microtask_index_sequence<Indices...>) {
+  // WebAssembly's `call_indirect` requires the callee type to exactly match the
+  // call site. Cast the variadic microtask_t to the fixed-arity signature that
+  // matches argc before invoking it.
+  using typed_microtask_t =
+      void (*)(int *, int *, microtask_argument_t<Indices>...);
+  (*(typed_microtask_t)pkfn)(gtid, tid, p_argv[Indices]...);
+}
+
+// Keep a bounded set of exact signatures for targets that cannot dynamically
+// construct a variable-argument microtask call.
 int __kmp_invoke_microtask(microtask_t pkfn, int gtid, int tid, int argc,
                            void *p_argv[]
 #if OMPT_SUPPORT
@@ -55,85 +43,54 @@ int __kmp_invoke_microtask(microtask_t pkfn, int gtid, int tid, int argc,
   *exit_frame_ptr = OMPT_GET_FRAME_ADDRESS(0);
 #endif
 
+#define KMP_INVOKE_MICROTASK_CASE(N)                                           \
+  case N:                                                                      \
+    invokeMicrotask(pkfn, &gtid, &tid, p_argv,                                 \
+                    make_microtask_index_sequence<N>::type{});                 \
+    break
+
   switch (argc) {
+    KMP_INVOKE_MICROTASK_CASE(0);
+    KMP_INVOKE_MICROTASK_CASE(1);
+    KMP_INVOKE_MICROTASK_CASE(2);
+    KMP_INVOKE_MICROTASK_CASE(3);
+    KMP_INVOKE_MICROTASK_CASE(4);
+    KMP_INVOKE_MICROTASK_CASE(5);
+    KMP_INVOKE_MICROTASK_CASE(6);
+    KMP_INVOKE_MICROTASK_CASE(7);
+    KMP_INVOKE_MICROTASK_CASE(8);
+    KMP_INVOKE_MICROTASK_CASE(9);
+    KMP_INVOKE_MICROTASK_CASE(10);
+    KMP_INVOKE_MICROTASK_CASE(11);
+    KMP_INVOKE_MICROTASK_CASE(12);
+    KMP_INVOKE_MICROTASK_CASE(13);
+    KMP_INVOKE_MICROTASK_CASE(14);
+    KMP_INVOKE_MICROTASK_CASE(15);
+    KMP_INVOKE_MICROTASK_CASE(16);
+    KMP_INVOKE_MICROTASK_CASE(17);
+    KMP_INVOKE_MICROTASK_CASE(18);
+    KMP_INVOKE_MICROTASK_CASE(19);
+    KMP_INVOKE_MICROTASK_CASE(20);
+    KMP_INVOKE_MICROTASK_CASE(21);
+    KMP_INVOKE_MICROTASK_CASE(22);
+    KMP_INVOKE_MICROTASK_CASE(23);
+    KMP_INVOKE_MICROTASK_CASE(24);
+    KMP_INVOKE_MICROTASK_CASE(25);
+    KMP_INVOKE_MICROTASK_CASE(26);
+    KMP_INVOKE_MICROTASK_CASE(27);
+    KMP_INVOKE_MICROTASK_CASE(28);
+    KMP_INVOKE_MICROTASK_CASE(29);
+    KMP_INVOKE_MICROTASK_CASE(30);
+    KMP_INVOKE_MICROTASK_CASE(31);
+    KMP_INVOKE_MICROTASK_CASE(32);
   default:
     fprintf(stderr, "Too many args to microtask: %d!\n", argc);
     fflush(stderr);
     exit(-1);
-  case 0:
-    (*(microtask_t0)pkfn)(&gtid, &tid);
-    break;
-  case 1:
-    (*(microtask_t1)pkfn)(&gtid, &tid, p_argv[0]);
-    break;
-  case 2:
-    (*(microtask_t2)pkfn)(&gtid, &tid, p_argv[0], p_argv[1]);
-    break;
-  case 3:
-    (*(microtask_t3)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2]);
-    break;
-  case 4:
-    (*(microtask_t4)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                          p_argv[3]);
-    break;
-  case 5:
-    (*(microtask_t5)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                          p_argv[3], p_argv[4]);
-    break;
-  case 6:
-    (*(microtask_t6)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                          p_argv[3], p_argv[4], p_argv[5]);
-    break;
-  case 7:
-    (*(microtask_t7)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                          p_argv[3], p_argv[4], p_argv[5], p_argv[6]);
-    break;
-  case 8:
-    (*(microtask_t8)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                          p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                          p_argv[7]);
-    break;
-  case 9:
-    (*(microtask_t9)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                          p_argv[3], p_argv[4], p_argv[5], p_argv[6], p_argv[7],
-                          p_argv[8]);
-    break;
-  case 10:
-    (*(microtask_t10)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                           p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                           p_argv[7], p_argv[8], p_argv[9]);
-    break;
-  case 11:
-    (*(microtask_t11)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                           p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                           p_argv[7], p_argv[8], p_argv[9], p_argv[10]);
-    break;
-  case 12:
-    (*(microtask_t12)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                           p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                           p_argv[7], p_argv[8], p_argv[9], p_argv[10],
-                           p_argv[11]);
-    break;
-  case 13:
-    (*(microtask_t13)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                           p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                           p_argv[7], p_argv[8], p_argv[9], p_argv[10],
-                           p_argv[11], p_argv[12]);
-    break;
-  case 14:
-    (*(microtask_t14)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                           p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                           p_argv[7], p_argv[8], p_argv[9], p_argv[10],
-                           p_argv[11], p_argv[12], p_argv[13]);
-    break;
-  case 15:
-    (*(microtask_t15)pkfn)(&gtid, &tid, p_argv[0], p_argv[1], p_argv[2],
-                           p_argv[3], p_argv[4], p_argv[5], p_argv[6],
-                           p_argv[7], p_argv[8], p_argv[9], p_argv[10],
-                           p_argv[11], p_argv[12], p_argv[13], p_argv[14]);
-    break;
   }
 
+#undef KMP_INVOKE_MICROTASK_CASE
+
   return 1;
 }
 
diff --git a/openmp/runtime/test/misc_bugs/microtask-32-args.c b/openmp/runtime/test/misc_bugs/microtask-32-args.c
new file mode 100644
index 0000000000000..848ab7c368481
--- /dev/null
+++ b/openmp/runtime/test/misc_bugs/microtask-32-args.c
@@ -0,0 +1,56 @@
+// RUN: %libomp-compile-and-run
+
+// Exercise the upper bound supported by the generic microtask dispatcher.
+
+int main(void) {
+  volatile int value0 = 0;
+  volatile int value1 = 1;
+  volatile int value2 = 2;
+  volatile int value3 = 3;
+  volatile int value4 = 4;
+  volatile int value5 = 5;
+  volatile int value6 = 6;
+  volatile int value7 = 7;
+  volatile int value8 = 8;
+  volatile int value9 = 9;
+  volatile int value10 = 10;
+  volatile int value11 = 11;
+  volatile int value12 = 12;
+  volatile int value13 = 13;
+  volatile int value14 = 14;
+  volatile int value15 = 15;
+  volatile int value16 = 16;
+  volatile int value17 = 17;
+  volatile int value18 = 18;
+  volatile int value19 = 19;
+  volatile int value20 = 20;
+  volatile int value21 = 21;
+  volatile int value22 = 22;
+  volatile int value23 = 23;
+  volatile int value24 = 24;
+  volatile int value25 = 25;
+  volatile int value26 = 26;
+  volatile int value27 = 27;
+  volatile int value28 = 28;
+  volatile int value29 = 29;
+  volatile int value30 = 30;
+  volatile int value31 = 31;
+
+#pragma omp parallel num_threads(2)                                            \
+    shared(value0, value1, value2, value3, value4, value5, value6, value7,     \
+               value8, value9, value10, value11, value12, value13, value14,    \
+               value15, value16, value17, value18, value19, value20, value21,  \
+               value22, value23, value24, value25, value26, value27, value28,  \
+               value29, value30, value31)
+  {
+    int sum = value0 + value1 + value2 + value3 + value4 + value5 + value6 +
+              value7 + value8 + value9 + value10 + value11 + value12 + value13 +
+              value14 + value15 + value16 + value17 + value18 + value19 +
+              value20 + value21 + value22 + value23 + value24 + value25 +
+              value26 + value27 + value28 + value29 + value30 + value31;
+    if (sum != 496)
+      __builtin_trap();
+  }
+
+  return 0;
+}
diff --git a/openmp/runtime/test/misc_bugs/wasm-microtask-33-args.c b/openmp/runtime/test/misc_bugs/wasm-microtask-33-args.c
new file mode 100644
index 0000000000000..bb88fcb239777
--- /dev/null
+++ b/openmp/runtime/test/misc_bugs/wasm-microtask-33-args.c
@@ -0,0 +1,60 @@
+// REQUIRES: wasm32-target-arch
+// RUN: %libomp-compile
+// RUN: %not %libomp-run 2>&1 | FileCheck %s
+
+// CHECK: Too many args to microtask: 33!
+
+int main(void) {
+  volatile int value0 = 0;
+  volatile int value1 = 1;
+  volatile int value2 = 2;
+  volatile int value3 = 3;
+  volatile int value4 = 4;
+  volatile int value5 = 5;
+  volatile int value6 = 6;
+  volatile int value7 = 7;
+  volatile int value8 = 8;
+  volatile int value9 = 9;
+  volatile int value10 = 10;
+  volatile int value11 = 11;
+  volatile int value12 = 12;
+  volatile int value13 = 13;
+  volatile int value14 = 14;
+  volatile int value15 = 15;
+  volatile int value16 = 16;
+  volatile int value17 = 17;
+  volatile int value18 = 18;
+  volatile int value19 = 19;
+  volatile int value20 = 20;
+  volatile int value21 = 21;
+  volatile int value22 = 22;
+  volatile int value23 = 23;
+  volatile int value24 = 24;
+  volatile int value25 = 25;
+  volatile int value26 = 26;
+  volatile int value27 = 27;
+  volatile int value28 = 28;
+  volatile int value29 = 29;
+  volatile int value30 = 30;
+  volatile int value31 = 31;
+  volatile int value32 = 32;
+
+#pragma omp parallel num_threads(2)                                            \
+    shared(value0, value1, value2, value3, value4, value5, value6, value7,     \
+               value8, value9, value10, value11, value12, value13, value14,    \
+               value15, value16, value17, value18, value19, value20, value21,  \
+               value22, value23, value24, value25, value26, value27, value28,  \
+               value29, value30, value31, value32)
+  {
+    int sum = value0 + value1 + value2 + value3 + value4 + value5 + value6 +
+              value7 + value8 + value9 + value10 + value11 + value12 + value13 +
+              value14 + value15 + value16 + value17 + value18 + value19 +
+              value20 + value21 + value22 + value23 + value24 + value25 +
+              value26 + value27 + value28 + value29 + value30 + value31 +
+              value32;
+    if (sum != 528)
+      __builtin_trap();
+  }
+
+  return 0;
+}



More information about the Openmp-commits mailing list