/*
 * SPDX-License-Identifier: GPL-3.0-only
 * This file is part of Lazuli.
 */

/**
 * @file
 * @brief Macro aliases on compiler facilities.
 * @copyright 2017-2020, Remi Andruccioli <remi.andruccioli@gmail.com>
 *
 * This file defines macros that can be used as aliases on compiler facilities
 * to add non-standard features to pure C89.
 * Ideally kernel code should work without these macros. These are just the
 * icing on the cake.
 */

#ifndef LAZULI_SYS_COMPILER_H
#define LAZULI_SYS_COMPILER_H

#ifdef __GNUC__

/*
 * The following macro functions definitions are an ugly hack to solve an ugly
 * compiler error: "section type conflict".
 *
 * TODO: Try a better way to solve this problem.
 */
/**
 * Turn a symbol into a string constant.
 *
 * @param X The symbol to turn into a string.
 */
#define COMPILER_H_STRINGIFY(X) #X

/**
 * Add a level of indirection to COMPILER_H_STRINGIFY() to force evaluation.
 *
 * @param X The symbol to turn into a string.
 */
#define COMPILER_H_TOSTRING(X) COMPILER_H_STRINGIFY(X)

/**
 * Generate a "unique" section name string that starts with ".progmem."
 * immediately followed by the line number.
 */
#define COMPILER_H_GENERATE_PROGMEM_SECTION_NAME() .progmem.__LINE__

/**
 * Indicates that a function never returns.
 */
#define NORETURN __attribute__((noreturn))

/*
 * We force the declaration of different ".progmem" sections by using all these
 * macro functions to eliminate the "section type conflict" error generated by
 * the compiler.
 * When linking, all these sections will be grouped into a unique ".progmem"
 * section.
 */
/**
 * Declare a variable to be stored in program memory.
 * i.e. usualy in ROM.
 *
 * Variables declared with PROGMEM must be retrieved from progmem with
 * Arch_LoadXXXFromProgmem() family of functions.
 */
#define PROGMEM                                                         \
  __attribute__                                                         \
  ((section(COMPILER_H_TOSTRING(COMPILER_H_GENERATE_PROGMEM_SECTION_NAME()))))

/**
 * Declare a global uninitialized variable to be excluded from ".bss" section.
 * In ISO C, all uninitialized global variables go to the ".bss" section, and
 * are initialized by default to zero at system startup.
 * Use this macro to prevent such a variable to be placed in ".bss" section and
 * thus be initialized by default.
 *
 * We use this macro with global variables that are manually initialized after
 * system startup, and that don't need to be automatically set to zero.
 *
 * @warning The behavior of declaring an initialized variable with NOINIT is
 * unknown.
 */
#define NOINIT __attribute__((section(".noinit")))

#else /* __GNUC__ */

#define NORETURN
#define PROGMEM
#define NOINIT

#endif/* __GNUC__ */

#endif /* LAZULI_SYS_COMPILER_H */
