[flang-commits] [flang] b6c2805 - [flang-rt] Add fatal diagnostics with integer values (#231241)

via flang-commits flang-commits at lists.llvm.org
Fri Oct 16 00:56:52 PDT 2026


Author: jeanPerier
Date: 2026-10-16T09:56:40+02:00
New Revision: b6c280535255ecd870f0df23a88fa1a0f279c16f

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

LOG: [flang-rt] Add fatal diagnostics with integer values (#231241)

Add noreturn runtime entries for messages with integer diagnostic
values. Use a fixed four-value signature for small messages and a
count-and-array signature for larger payloads, keeping both callable
from device code.

The array entry accepts the restricted %lld/%% format, checks argument
counts and allocation sizes, and preserves the existing source location,
crash handler, and termination behavior. Document the runtime interfaces
in the inline-check design and cover formatting and failures in unit
tests.

Assisted-by: AI

Added: 
    

Modified: 
    flang-rt/lib/runtime/stop.cpp
    flang-rt/unittests/Runtime/Stop.cpp
    flang/docs/InlineRuntimeChecks.md
    flang/include/flang/Runtime/stop.h

Removed: 
    


################################################################################
diff  --git a/flang-rt/lib/runtime/stop.cpp b/flang-rt/lib/runtime/stop.cpp
index b4508e0b7d0bc..561ea41642aaf 100644
--- a/flang-rt/lib/runtime/stop.cpp
+++ b/flang-rt/lib/runtime/stop.cpp
@@ -245,8 +245,65 @@ static RT_NOINLINE_ATTR void PrintBacktrace() {
 
 RT_OPTNONE_ATTR void FORTRAN_PROCEDURE_NAME(backtrace)() { PrintBacktrace(); }
 
+RT_EXT_API_GROUP_BEGIN
 [[noreturn]] RT_API_ATTRS void RTNAME(ReportFatalUserError)(
     const char *message, const char *source, int line) {
   Fortran::runtime::Terminator{source, line}.Crash(message);
 }
+
+[[noreturn]] RT_API_ATTRS void RTNAME(ReportFatalUserErrorValues)(
+    const char *message, const char *source, int line, long long v0,
+    long long v1, long long v2, long long v3) {
+  Fortran::runtime::Terminator{source, line}.Crash(message, v0, v1, v2, v3);
+}
+
+[[noreturn]] RT_API_ATTRS void RTNAME(ReportFatalUserErrorValueArray)(
+    const char *message, const char *source, int line, std::size_t count,
+    const long long *values) {
+  Fortran::runtime::Terminator terminator{source, line};
+  RUNTIME_CHECK(terminator, message && (count == 0 || values));
+
+  // Validate the format and count before printing the diagnostic or reading
+  // values.
+  std::size_t conversions{0};
+  for (const char *p{message}; *p; ++p) {
+    if (*p == '%') {
+      if (p[1] == '%') {
+        ++p;
+      } else if (p[1] == 'l' && p[2] == 'l' && p[3] == 'd') {
+        ++conversions;
+        p += 3;
+      } else {
+        terminator.Crash("Invalid fatal error message format");
+      }
+    }
+  }
+  if (conversions != count) {
+    terminator.Crash("Fatal error message value count mismatch");
+  }
+
+  // Print in pieces so reporting the error does not require allocating a
+  // formatted message. Concurrent failures may interleave their output.
+  terminator.CrashHeader();
+  std::size_t nextValue{0};
+  for (const char *p{message}; *p;) {
+    if (*p != '%') {
+      char chunk[256];
+      std::size_t length{0};
+      while (*p && *p != '%' && length < sizeof chunk - 1) {
+        chunk[length++] = *p++;
+      }
+      chunk[length] = '\0';
+      terminator.PrintCrashArgs("%s", chunk);
+    } else if (p[1] == '%') {
+      terminator.PrintCrashArgs("%%");
+      p += 2;
+    } else {
+      terminator.PrintCrashArgs("%lld", values[nextValue++]);
+      p += 4;
+    }
+  }
+  terminator.CrashFooter();
+}
+RT_EXT_API_GROUP_END
 }

diff  --git a/flang-rt/unittests/Runtime/Stop.cpp b/flang-rt/unittests/Runtime/Stop.cpp
index 5c30f6c301cd3..0facf6c8345a6 100644
--- a/flang-rt/unittests/Runtime/Stop.cpp
+++ b/flang-rt/unittests/Runtime/Stop.cpp
@@ -13,9 +13,11 @@
 #include "flang/Runtime/stop.h"
 #include "CrashHandlerFixture.h"
 #include "flang-rt/runtime/environment.h"
+#include "flang-rt/runtime/terminator.h"
 #include <cstdlib>
 #include <cstring>
 #include <gtest/gtest.h>
+#include <limits>
 
 using namespace Fortran::runtime;
 
@@ -128,3 +130,82 @@ TEST_F(TestProgramEnd, CrashTest) {
       RTNAME(ReportFatalUserError)(crashMessage.c_str(), fileName.c_str(), 343),
       fullMessage.c_str());
 }
+
+TEST_F(TestProgramEnd, CrashValuesTest) {
+  EXPECT_DEATH(
+      RTNAME(ReportFatalUserErrorValues)("sizes %lld, %lld, %lld, %lld %%",
+          "check.f90", 42, -17, 4294967296LL, 0, 9),
+      "check.f90\\(42\\): sizes -17, 4294967296, 0, 9 %");
+}
+
+TEST_F(TestProgramEnd, CrashValuesWithoutValuesTest) {
+  EXPECT_DEATH(RTNAME(ReportFatalUserErrorValues)(
+                   "bad argument: 100%%", "check.f90", 7, 0, 0, 0, 0),
+      "check.f90\\(7\\): bad argument: 100%");
+}
+
+TEST_F(TestProgramEnd, CrashValueArrayTest) {
+  const long long values[]{std::numeric_limits<long long>::min(),
+      std::numeric_limits<long long>::max(), 0, -1, 4294967296LL, 17};
+  EXPECT_DEATH(
+      RTNAME(ReportFatalUserErrorValueArray)(
+          "%lld %lld %lld %lld %lld %%%lld %%lld", "check.f90", 43, 6, values),
+      "fatal Fortran runtime error\\(check.f90:43\\): "
+      "-9223372036854775808 9223372036854775807 0 -1 "
+      "4294967296 %17 %lld");
+}
+
+TEST_F(TestProgramEnd, CrashValueArrayWithoutValuesTest) {
+  EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                   "bad argument: 100%%", "check.f90", 7, 0, nullptr),
+      "fatal Fortran runtime error\\(check.f90:7\\): bad argument: 100%");
+  EXPECT_DEATH(
+      RTNAME(ReportFatalUserErrorValueArray)("", "check.f90", 7, 0, nullptr),
+      "fatal Fortran runtime error\\(check.f90:7\\): ");
+}
+
+TEST_F(TestProgramEnd, CrashValueArrayInvalidFormatTest) {
+  for (const char *message : {"%", "%l", "%ll", "%d", "%n", "%1$lld"}) {
+    EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                     message, "check.f90", 7, 0, nullptr),
+        "Invalid fatal error message format");
+  }
+}
+
+TEST_F(TestProgramEnd, CrashValueArrayCountMismatchTest) {
+  const long long values[]{1, 2};
+  EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                   "%lld %lld", "check.f90", 7, 1, values),
+      "Fatal error message value count mismatch");
+  EXPECT_DEATH(
+      RTNAME(ReportFatalUserErrorValueArray)("%lld", "check.f90", 7, 2, values),
+      "Fatal error message value count mismatch");
+  EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                   "%lld", "check.f90", 7, 0, nullptr),
+      "Fatal error message value count mismatch");
+  EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                   "%lld", "check.f90", 7, 1, nullptr),
+      "RUNTIME_CHECK");
+}
+
+TEST_F(TestProgramEnd, CrashValueArrayLargeCountMismatchTest) {
+  const long long value{0};
+  for (std::size_t count : {std::numeric_limits<std::size_t>::max(),
+           std::numeric_limits<std::size_t>::max() / 64}) {
+    EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                     "%lld", "check.f90", 7, count, &value),
+        "Fatal error message value count mismatch");
+  }
+}
+
+TEST_F(TestProgramEnd, CrashValueArrayStandardDiagnosticTest) {
+  // A long literal must span several chunks without truncation.
+  std::string message(4096, 'x');
+  message += " %lld %lld %lld %lld %lld %%";
+  const long long values[]{-1, 0, 1, 2, 3};
+  EXPECT_DEATH(RTNAME(ReportFatalUserErrorValueArray)(
+                   message.c_str(), "check.f90", 99, 5, values),
+      ("fatal Fortran runtime error\\(check.f90:99\\): " +
+          std::string(4096, 'x') + " -1 0 1 2 3 %")
+          .c_str());
+}

diff  --git a/flang/docs/InlineRuntimeChecks.md b/flang/docs/InlineRuntimeChecks.md
index 7a7e2892036cf..1d1d77bf924f4 100644
--- a/flang/docs/InlineRuntimeChecks.md
+++ b/flang/docs/InlineRuntimeChecks.md
@@ -255,12 +255,18 @@ llvm.cond_br %ok weights([2000, 1]), ^cont, ^fail
 
 - **Runtime entry.** `_FortranAReportFatalUserError(msg, source, line)`
   (`flang/include/flang/Runtime/stop.h`) is `[[noreturn]]` and device-callable
-  (`RT_API_ATTRS`). It takes no values. Proposed addition:
-  `ReportFatalUserErrorValues(msg, source, line, v0..v3)` with a fixed
-  number of `long long` arguments. That keeps the ABI simple on devices, and
-  messages use `%lld` only (CUDA's device `printf` only documents the `h`,
-  `l`, and `ll` length modifiers). The message is passed to `printf`, so a
-  literal `%` in a message must be escaped.
+  (`RT_API_ATTRS`). It takes no values. Assertions with up to four values use
+  `ReportFatalUserErrorValues(msg, source, line, v0..v3)` with fixed
+  `long long` arguments, padding unused arguments with zeros. Assertions with
+  more values use `ReportFatalUserErrorValueArray(msg, source, line, count,
+  values)`, where `count` is a `size_t` and `values` points to an array of
+  `long long`. Code generation packs that array on the failure path; the
+  assertion's diagnostic operands remain SSA values until then. Both entries
+  have fixed signatures to keep the ABI simple on devices. Messages use only
+  `%lld` and `%%` (for a literal percent sign). The array entry prints the
+  diagnostic in pieces through the existing runtime termination machinery,
+  without allocating a formatted message. Concurrent failures may interleave
+  their output.
 - **Declaration.** The runtime function declaration must carry `noreturn`,
   so LLVM can use the checked condition on the continuation path.
 - **Source location.** Take it from the assert's own location. Resolve
@@ -778,10 +784,11 @@ elimination do not. The code generation pattern must work on unoptimized IR.
 
 ## 9. Open questions
 
-1. Message formatting: a fixed number of `long long` values, or a variadic
-   runtime entry? Inline messages keep the runtime's text as closely as
-   possible, and only 
diff er where the inline check is stricter (per-dimension
-   conformance, section 5.1).
+1. Diagnostic detail: report only the failing dimension, or include complete
+   shapes and subscript tuples? The fixed and count-and-array entries support
+   both. Inline messages keep the runtime's text as closely as possible, and
+   only 
diff er where the inline check is stricter (per-dimension conformance,
+   section 5.1).
 2. When a default-on kind (`conformance`, `argument`) is disabled by an
    option, should its checks be emitted as assumptions (`llvm.intr.assume` at
    code generation) instead of not at all? An assumption gives more

diff  --git a/flang/include/flang/Runtime/stop.h b/flang/include/flang/Runtime/stop.h
index 710d75494c875..495dad06c4266 100644
--- a/flang/include/flang/Runtime/stop.h
+++ b/flang/include/flang/Runtime/stop.h
@@ -41,9 +41,27 @@ void FORTRAN_PROCEDURE_NAME(backtrace)(NO_ARGUMENTS);
 
 // Crash with an error message when the program dynamically violates a Fortran
 // constraint.
+RT_EXT_API_GROUP_BEGIN
 NORETURN RT_API_ATTRS void RTNAME(ReportFatalUserError)(
     const char *message, const char *source, int line);
 
+// The caller must provide a NUL-terminated message containing at most four
+// %lld conversions, with any other percent signs escaped as %%. This contract
+// is not checked. A fixed argument list also permits calls from device code;
+// unused values are ignored.
+NORETURN RT_API_ATTRS void RTNAME(ReportFatalUserErrorValues)(
+    const char *message, const char *source, int line, long long v0,
+    long long v1, long long v2, long long v3);
+
+// The same NUL-terminated restricted format with an arbitrary number of values.
+// The number of %lld conversions must equal count; values may be null when
+// count is zero. The diagnostic is printed in pieces without allocating a
+// formatted message, so concurrent failures may interleave their output.
+NORETURN RT_API_ATTRS void RTNAME(ReportFatalUserErrorValueArray)(
+    const char *message, const char *source, int line, size_t count,
+    const long long *values);
+RT_EXT_API_GROUP_END
+
 FORTRAN_EXTERN_C_END
 
 #endif // FORTRAN_RUNTIME_STOP_H_


        


More information about the flang-commits mailing list