//===-- runtime/terminator.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Termination of the image
#ifndef FORTRAN_RUNTIME_TERMINATOR_H_
#define FORTRAN_RUNTIME_TERMINATOR_H_
#include "flang/Common/api-attrs.h"
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
namespace Fortran::runtime {
// A mixin class for statement-specific image error termination
// for errors detected in the runtime library
class Terminator {
public:
RT_API_ATTRS Terminator() {}
Terminator(const Terminator &) = default;
explicit RT_API_ATTRS Terminator(
const char *sourceFileName, int sourceLine = 0)
: sourceFileName_{sourceFileName}, sourceLine_{sourceLine} {}
RT_API_ATTRS const char *sourceFileName() const { return sourceFileName_; }
RT_API_ATTRS int sourceLine() const { return sourceLine_; }
RT_API_ATTRS void SetLocation(
const char *sourceFileName = nullptr, int sourceLine = 0) {
sourceFileName_ = sourceFileName;
sourceLine_ = sourceLine;
}
// Silence compiler warnings about the format string being
// non-literal. A more precise control would be
// __attribute__((format_arg(2))), but it requires the function
// to return 'char *', which does not work well with noreturn.
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
// Device offload compilers do not normally support varargs and va_list,
// so use C++ variadic templates to forward the crash arguments
// to regular printf for the device compilation.
// Try to keep the inline implementations as small as possible.
template <typename... Args>
[[noreturn]] RT_DEVICE_NOINLINE RT_API_ATTRS const char *Crash(
const char *message, Args... args) const {
#if !defined(RT_DEVICE_COMPILATION)
// Invoke handler set up by the test harness.
InvokeCrashHandler(message, args...);
#endif
CrashHeader();
PrintCrashArgs(message, args...);
CrashFooter();
}
template <typename... Args>
RT_API_ATTRS void PrintCrashArgs(const char *message, Args... args) const {
#if defined(RT_DEVICE_COMPILATION)
std::printf(message, args...);
#else
std::fprintf(stderr, message, args...);
#endif
}
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
RT_API_ATTRS void CrashHeader() const;
[[noreturn]] RT_API_ATTRS void CrashFooter() const;
#if !defined(RT_DEVICE_COMPILATION)
void InvokeCrashHandler(const char *message, ...) const;
[[noreturn]] void CrashArgs(const char *message, va_list &) const;
#endif
[[noreturn]] RT_API_ATTRS void CheckFailed(
const char *predicate, const char *file, int line) const;
[[noreturn]] RT_API_ATTRS void CheckFailed(const char *predicate) const;
// For test harnessing - overrides CrashArgs().
static void RegisterCrashHandler(void (*)(const char *sourceFile,
int sourceLine, const char *message, va_list &ap));
private:
const char *sourceFileName_{nullptr};
int sourceLine_{0};
};
// RUNTIME_CHECK() guarantees evaluation of its predicate.
#define RUNTIME_CHECK(terminator, pred) \
if (pred) \
; \
else \
(terminator).CheckFailed(#pred, __FILE__, __LINE__)
#define INTERNAL_CHECK(pred) \
if (pred) \
; \
else \
Terminator{__FILE__, __LINE__}.CheckFailed(#pred)
RT_API_ATTRS void NotifyOtherImagesOfNormalEnd();
RT_API_ATTRS void NotifyOtherImagesOfFailImageStatement();
RT_API_ATTRS void NotifyOtherImagesOfErrorTermination();
} // namespace Fortran::runtime
namespace Fortran::runtime::io {
RT_API_ATTRS void FlushOutputOnCrash(const Terminator &);
}
#endif // FORTRAN_RUNTIME_TERMINATOR_H_