[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