Files
2024-05-03 21:52:40 +02:00

199 lines
6.5 KiB
C

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "revng/Support/ExplicitSpecializations.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#if !__has_builtin(__builtin_assume)
#define __builtin_assume(x)
#endif
#if !__has_builtin(__builtin_unreachable) && !defined(__GNUC__)
#define __builtin_unreachable(x)
#endif
#if defined(__clang__)
#define SILENCE_ASSUME_HEADER \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wassume\"")
#define SILENCE_ASSUME_FOOTER _Pragma("clang diagnostic pop")
#else
#define SILENCE_ASSUME_HEADER
#define SILENCE_ASSUME_FOOTER
#endif
/// Same as __builtin_assume but (temporarily) suppresses warnings about ignored
/// side-effects
#define silent_assume(what) \
do { \
SILENCE_ASSUME_HEADER \
__builtin_assume(static_cast<bool>(what)); \
SILENCE_ASSUME_FOOTER \
} while (0)
// We support C++11, C99 (with GNU attributes) or C11
#ifdef __cplusplus
#define boolcast(what) static_cast<bool>(what)
#define noret [[noreturn]]
#else
#define boolcast(what) (bool) (what)
#if __STDC_VERSION__ >= 201112L
#include <stdnoreturn.h>
#define noret noreturn
#elif __has_attribute(noreturn) || defined(__GNUC__)
#define noret __attribute__((noreturn))
#else
#warning "Can't mark functions as noreturn"
#endif
#endif
noret void revng_assert_fail(const char *AssertionBody,
const char *Message,
const char *File,
unsigned Line);
noret void revng_check_fail(const char *CheckBody,
const char *Message,
const char *File,
unsigned Line);
noret void revng_do_abort(const char *Message, const char *File, unsigned Line);
#undef noret
/// Aborts program execution with a message, in release mode too.
///
/// Use this macro to ensure program termination in case of an unexpected
/// situation.
#define revng_abort_impl(message) \
do { \
revng_do_abort(message, __FILE__, __LINE__); \
} while (0)
/// Asserts \a what or aborts with \a message, in release mode too.
///
/// Use this macro to ensure program termination in case of an unexpected
/// situation.
#define revng_check_impl(what, message) \
do { \
bool Condition = boolcast(what); \
if (!Condition) { \
revng_check_fail(#what, message, __FILE__, __LINE__); \
} \
silent_assume(Condition); \
} while (0)
#ifndef NDEBUG
/// Marks a program path as unreachable. In debug mode, aborts with \a message.
///
/// Use this macro to catch bugs during development an tell the compiler that a
/// certain situation will never happen at run-time, which might open up to new
/// optimization opportunities.
#define revng_unreachable_impl(message) revng_abort(message)
/// Asserts \a what or, in debug mode, aborts with \a message.
///
/// Use this macro to catch bugs during development an tell the compiler that a
/// certain situation will never happen at run-time, which might open up to new
/// optimization opportunities.
#define revng_assert_impl(what, message) \
do { \
bool Condition = boolcast(what); \
if (!Condition) { \
revng_assert_fail(#what, message, __FILE__, __LINE__); \
} \
silent_assume(Condition); \
} while (0)
#else
#define revng_unreachable_impl(message) __builtin_unreachable()
#define revng_assert_impl(what, message) \
do { \
(void) sizeof((what)); \
silent_assume(what); \
} while (0)
#endif
#define COMMA_IF_INVOKED(...) ,
#define CONCAT_TOKENS(a, b) a##b
#define CONCAT2(a, b) CONCAT_TOKENS(a, b)
#define CONCAT3(a, b, c) CONCAT2(CONCAT2(a, b), c)
#define CONCAT4(a, b, c, d) CONCAT2(CONCAT3(a, b, c), d)
#define CONCAT5(a, b, c, d, e) CONCAT2(CONCAT4(a, b, c, d), e)
#define GET_10TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _10
/// Check if the argument list contains a comma
#define HAS_COMMA(...) GET_10TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0)
/// Check if the argument list is empty
///
/// This is a workaround to avoid using ##__VA_ARGS__ (which eats the previous
/// comma if empty), since it's a GNU extension, and to void using default
/// arguments, since we want this header to be functioncal in C files too.
#define IS_EMPTY(...) \
HAS_COMMA(CONCAT5(IS_EMPTY_, \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(COMMA_IF_INVOKED __VA_ARGS__), \
HAS_COMMA(__VA_ARGS__()), \
HAS_COMMA(COMMA_IF_INVOKED __VA_ARGS__())))
// It's empty if the first three conditions are false and the last is true
#define IS_EMPTY_0001 ,
#define REVNG_ABORT_0(...) revng_abort_impl(__VA_ARGS__)
#define REVNG_ABORT_1(...) revng_abort_impl(NULL)
#define revng_abort(...) \
CONCAT2(REVNG_ABORT_, IS_EMPTY(__VA_ARGS__))(__VA_ARGS__)
#define REVNG_UNREACHABLE_0(...) revng_unreachable_impl(__VA_ARGS__)
#define REVNG_UNREACHABLE_1(...) revng_unreachable_impl(NULL)
#define revng_unreachable(...) \
CONCAT2(REVNG_UNREACHABLE_, IS_EMPTY(__VA_ARGS__))(__VA_ARGS__)
#define MACRO_OVERLOAD_1_OR_2(_1, _2, NAME, ...) NAME
#define revng_assert_impl_nomsg(what) revng_assert_impl(what, NULL)
#define revng_assert(...) \
MACRO_OVERLOAD_1_OR_2(__VA_ARGS__, \
revng_assert_impl, \
revng_assert_impl_nomsg) \
(__VA_ARGS__)
#define revng_check_impl_nomsg(what) revng_check_impl(what, NULL)
#define revng_check(...) \
MACRO_OVERLOAD_1_OR_2(__VA_ARGS__, revng_check_impl, revng_check_impl_nomsg) \
(__VA_ARGS__)
#ifdef __cplusplus
}
#endif