// GNU D Compiler attribute support declarations.
// Copyright (C) 2021 Free Software Foundation, Inc.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

module gcc.attributes;

// Private helper templates.
private struct Attribute(A...)
{
    A arguments;
}

private enum bool isStringValue(alias T) = is(typeof(T) == string);

private enum bool isStringOrIntValue(alias T)
    = is(typeof(T) == string) || is(typeof(T) == int);

private template allSatisfy(alias F, T...)
{
    static if (T.length == 0)
        enum allSatisfy = true;
    else static if (T.length == 1)
        enum allSatisfy = F!(T[0]);
    else
    {
        enum allSatisfy = allSatisfy!(F, T[ 0  .. $/2])
            && allSatisfy!(F, T[$/2 ..  $ ]);
    }
}

/**
 * Generic entrypoint for applying GCC attributes to a function or type.
 * There is no type checking done, as well as no deprecation path for
 * attributes removed from the compiler.  So the recommendation is to use any
 , of the other UDAs available unless it is a target-specific attribute.
 *
 * Function attributes introduced by the @attribute UDA are used in the
 * declaration of a function, followed by an attribute name string and
 * any arguments separated by commas enclosed in parentheses.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @attribute("regparm", 1) int func(int size);
 * ---
 */
@system
auto attribute(A...)(A arguments)
    if (A.length > 0 && is(A[0] == string))
{
    return Attribute!A(arguments);
}


///////////////////////////////////////////////////////////////////////////////
//
// Supported common attributes exposed by GDC.
//
///////////////////////////////////////////////////////////////////////////////

/**
 * The `@alloc_size` attribute may be applied to a function that returns a
 * pointer and takes at least one argument of an integer or enumerated type.
 * It indicates that the returned pointer points to memory whose size is given
 * by the function argument at `sizeArgIdx`, or by the product of the arguments
 * at `sizeArgIdx` and `numArgIdx`.  Meaningful sizes are positive values less
 * than `ptrdiff_t.max`.  Unless `zeroBasedNumbering` is true, argument
 * numbering starts at one for ordinary functions, and at two for non-static
 * member functions.
 *
 * If `numArgIdx` is less than `0`, it is taken to mean there is no argument
 * specifying the element count.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @alloc_size(1) extern(C) void* malloc(size_t size);
 * @alloc_size(3,2) extern(C) void* reallocarray(void *ptr, size_t nmemb,
 *                                               size_t size);
 * @alloc_size(1,2) void* my_calloc(size_t element_size, size_t count,
 *                                  bool irrelevant);
 * ---
 */
auto alloc_size(int sizeArgIdx)
{
    return attribute("alloc_size", sizeArgIdx);
}

/// ditto
auto alloc_size(int sizeArgIdx, int numArgIdx)
{
    return attribute("alloc_size", sizeArgIdx, numArgIdx);
}

/// ditto
auto alloc_size(int sizeArgIdx, int numArgIdx, bool zeroBasedNumbering)
{
    return attribute("alloc_size", sizeArgIdx, numArgIdx, zeroBasedNumbering);
}

auto alloc_size(A...)(A arguments)
{
    assert(false, "alloc_size attribute argument value is not an integer constant");
}

/**
 * The `@always_inline` attribute inlines the function independent of any
 * restrictions that otherwise apply to inlining.  Failure to inline such a
 * function is diagnosed as an error.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @always_inline int func();
 * ---
 */
enum always_inline = attribute("always_inline");

/**
 * The `@cold` attribute on functions is used to inform the compiler that the
 * function is unlikely to be executed.  The function is optimized for size
 * rather than speed and on many targets it is placed into a special subsection
 * of the text section so all cold functions appear close together, improving
 * code locality of non-cold parts of program.  The paths leading to calls of
 * cold functions within code are considered to be cold too.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @cold int func();
 * ---
 */
enum cold = attribute("cold");

/**
 * The `@flatten` attribute is used to inform the compiler that every call
 * inside this function should be inlined, if possible.  Functions declared with
 * attribute `@noinline` and similar are not inlined.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @flatten int func();
 * ---
 */
enum flatten = attribute("flatten");

/**
 * The `@no_icf` attribute prevents a functions from being merged with another
 * semantically equivalent function.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @no_icf int func();
 * ---
 */
enum no_icf = attribute("no_icf");

/**
 * The `@noclone` attribute prevents a function from being considered for
 * cloning - a mechanism that produces specialized copies of functions and
 * which is (currently) performed by interprocedural constant propagation.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @noclone int func();
 * ---
 */
enum noclone = attribute("noclone");

/**
 * The `@noinline` attribute prevents a function from being considered for
 * inlining.  If the function does not have side effects, there are
 * optimizations other than inlining that cause function calls to be optimized
 * away, although the function call is live.  To keep such calls from being
 * optimized away, put `asm { ""; }` in the called function, to serve as a
 * special side effect.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @noinline int func();
 * ---
 */
enum noinline = attribute("noinline");

/**
 * The `@noipa` attribute disables interprocedural optimizations between the
 * function with this attribute and its callers, as if the body of the function
 * is not available when optimizing callers and the callers are unavailable when
 * optimizing the body.  This attribute implies `@noinline`, `@noclone`, and
 * `@no_icf` attributes.  However, this attribute is not equivalent to a
 * combination of other attributes, because its purpose is to suppress existing
 * and future optimizations employing interprocedural analysis, including those
 * that do not have an attribute suitable for disabling them individually.
 *
 * This attribute is supported mainly for the purpose of testing the compiler.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @noipa int func();
 * ---
 */
enum noipa = attribute("noipa");

/**
 * The `@optimize` attribute is used to specify that a function is to be
 * compiled with different optimization options than specified on the command
 * line.  Valid `arguments` are constant non-negative integers and strings.
 * Multiple arguments can be provided, separated by commas to specify multiple
 * options.  Each numeric argument specifies an optimization level.  Each string
 * argument that begins with the letter O refers to an optimization option such
 * as `-O0` or `-Os`.  Other options are taken as suffixes to the `-f` prefix
 * jointly forming the name of an optimization option.
 *
 * Not every optimization option that starts with the `-f` prefix specified by
 * the attribute necessarily has an effect on the function.  The `@optimize`
 * attribute should be used for debugging purposes only.  It is not suitable in
 * production code.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @optimize(2) double fn0(double x);
 * @optimize("2") double fn1(double x);
 * @optimize("s") double fn2(double x);
 * @optimize("Ofast") double fn3(double x);
 * @optimize("-O2") double fn4(double x);
 * @optimize("tree-vectorize") double fn5(double x);
 * @optimize("-ftree-vectorize") double fn6(double x);
 * @optimize("no-finite-math-only", 3) double fn7(double x);
 * ---
 */
auto optimize(A...)(A arguments)
    if (allSatisfy!(isStringOrIntValue, arguments))
{
    return attribute("optimize", arguments);
}

auto optimize(A...)(A arguments)
    if (!allSatisfy!(isStringOrIntValue, arguments))
{
    assert(false, "optimize attribute argument not a string or integer constant");
}

/**
 * The `@restrict` attribute specifies that a function parameter is to be
 * restrict-qualified in the C99 sense of the term.  The parameter needs to
 * boil down to either a pointer or reference type, such as a D pointer,
 * class reference, or a `ref` parameter.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * void func(@restrict ref const float[16] array);
 * ---
 */
enum restrict = attribute("restrict");

/**
 * The `@section` attribute specifies that a function lives in a particular
 * section.  For when you need certain particular functions to appear in
 * special sections.
 *
 * Some file formats do not support arbitrary sections so the section attribute
 * is not available on all platforms.  If you need to map the entire contents
 * of a module to a particular section, consider using the facilities of the
 * linker instead.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @section("bar") extern void func();
 * ---
 */
auto section(string sectionName)
{
    return attribute("section", sectionName);
}

auto section(A...)(A arguments)
{
    assert(false, "section attribute argument not a string constant");
}

/**
 * The `@symver` attribute creates a symbol version on ELF targets.  The syntax
 * of the string parameter is `name@nodename`.  The `name` part of the parameter
 * is the actual name of the symbol by which it will be externally referenced.
 * The `nodename` portion should be the name of a node specified in the version
 * script supplied to the linker when building a shared library.  Versioned
 * symbol must be defined and must be exported with default visibility.
 *
 * Finally if the parameter is `name@@nodename` then in addition to creating a
 * symbol version (as if `name@nodename` was used) the version will be also used
 * to resolve `name` by the linker.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @symver("foo@VERS_1") int foo_v1();
 * ---
 */
auto symver(A...)(A arguments)
    if (allSatisfy!(isStringValue, arguments))
{
    return attribute("symver", arguments);
}

auto symver(A...)(A arguments)
    if (!allSatisfy!(isStringValue, arguments))
{
    assert(false, "symver attribute argument not a string constant");
}

/**
 * The `@target` attribute is used to specify that a function is to be
 * compiled with different target options than specified on the command line.
 * One or more strings can be provided as arguments, separated by commas to
 * specify multiple options.  Each string consists of one or more
 * comma-separated suffixes to the `-m` prefix jointly forming the name of a
 * machine-dependent option.
 *
 * The target attribute can be used for instance to have a function compiled
 * with a different ISA (instruction set architecture) than the default.
 *
 * The options supported are specific to each target.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @target("arch=core2") void core2_func();
 * @target("sse3") void sse3_func();
 * ---
 */
auto target(A...)(A arguments)
    if (allSatisfy!(isStringValue, arguments))
{
    return attribute("target", arguments);
}

auto target(A...)(A arguments)
    if (!allSatisfy!(isStringValue, arguments))
{
    assert(false, "target attribute argument not a string constant");
}

/**
 * The `@target_clones` attribute is used to specify that a function be cloned
 * into multiple versions compiled with different target `options` than
 * specified on the command line.  The supported options and restrictions are
 * the same as for `@target` attribute.
 *
 * It also creates a resolver function that dynamically selects a clone suitable
 * for current architecture.  The resolver is created only if there is a usage
 * of a function with `@target_clones` attribute.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @target_clones("sse4.1,avx,default") double func(double x);
 * ---
 */
auto target_clones(A...)(A arguments)
    if (allSatisfy!(isStringValue, arguments))
{
    return attribute("target_clones", arguments);
}

auto target_clones(A...)(A arguments)
    if (!allSatisfy!(isStringValue, arguments))
{
    assert(false, "target attribute argument not a string constant");
}

/**
 * The `@used` attribute, annotated to a function, means that code must be
 * emitted for the function even if it appears that the function is not
 * referenced.  This is useful, for example, when the function is referenced
 * only in inline assembly.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @used __gshared int var = 0x1000;
 * ---
 */
enum used = attribute("used");

/**
 * The `@weak` attribute causes a declaration of an external symbol to be
 * emitted as a weak symbol rather than a global.  This is primarily useful in
 * defining library functions that can be overridden in user code, though it can
 * also be used with non-function declarations.  The overriding symbol must have
 * the same type as the weak symbol.  In addition, if it designates a variable
 * it must also have the same size and alignment as the weak symbol.
 *
 * Weak symbols are supported for ELF targets, and also for a.out targets when
 * using the GNU assembler and linker.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @weak int func() { return 1; }
 * ---
 */
enum weak = attribute("weak");

/**
 * The `@noplt` attribute is the counterpart to option `-fno-plt`. Calls to
 * functions marked with this attribute in position-independent code do not use
 * the PLT in position-independent code.
 *
 * In position-dependant code, a few targets also convert call to functions
 * that are marked to not use the PLT to use the GOT instead.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @noplt int func();
 *
 * ---
 */
enum noplt = attribute("noplt");

///////////////////////////////////////////////////////////////////////////////
//
// Attributes defined for compatibility with LDC.
//
///////////////////////////////////////////////////////////////////////////////

/**
 * Specifies that the function returns `null` or a pointer to at least a
 * certain number of allocated bytes. `sizeArgIdx` and `numArgIdx` specify
 * the 0-based index of the function arguments that should be used to calculate
 * the number of bytes returned.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @allocSize(0) extern(C) void* malloc(size_t size);
 * @allocSize(2,1) extern(C) void* reallocarray(void *ptr, size_t nmemb,
 *                                              size_t size);
 * @allocSize(0,1) void* my_calloc(size_t element_size, size_t count,
 *                                 bool irrelevant);
 * ---
 */
auto allocSize(int sizeArgIdx, int numArgIdx = int.min)
{
    return alloc_size(sizeArgIdx, numArgIdx, true);
}

auto allocSize(A...)(A arguments)
{
    assert(false, "allocSize attribute argument value is not an integer constant");
}

/**
 * When applied to a global symbol, the compiler, assembler, and linker are
 * required to treat the symbol as if there is a reference to the symbol that
 * it cannot see (which is why they have to be named).  For example, it
 * prevents the deletion by the linker of an unreferenced symbol.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @assumeUsed __gshared int var = 0x1000;
 * ---
 */
alias assumeUsed = used;

/// This attribute has no effect.
enum dynamicCompile = false;

/// ditto
enum dynamicCompileConst = false;

/// ditto
enum dynamicCompileEmit = false;

/**
 * Explicitly sets "fast-math" for a function, enabling aggressive math
 * optimizations.  These optimizations may dramatically change the outcome of
 * floating point calculations (e.g. because of reassociation).
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @fastmath
 * double dot(double[] a, double[] b) {
 *     double s = 0;
 *     foreach(size_t i; 0..a.length)
 *     {
 *         // will result in vectorized fused-multiply-add instructions
 *         s += a * b;
 *     }
 *     return s;
 * }
 * ---
 */
enum fastmath = optimize("Ofast");

/**
 * Adds GCC's "naked" attribute to a function, disabling function prologue /
 * epilogue emission.
 * Intended to be used in combination with basic `asm` statement.  While using
 * extended `asm` or a mixture of basic `asm` and D code may appear to work,
 * they cannot be depended upon to work reliably and are not supported.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @naked void abort() {
 *     asm { "ud2"; }
 * }
 * ---
 */
enum naked = attribute("naked");

/**
 * Sets the optimization strategy for a function.
 * Valid strategies are "none", "optsize", "minsize". The strategies are
 * mutually exclusive.
 *
 * Example:
 * ---
 * import gcc.attributes;
 *
 * @optStrategy("none")
 * int func() {
 *     return call();
 * }
 * ---
 */
auto optStrategy(string strategy)
{
    if (strategy == "none")
        return optimize("O0");
    else if (strategy == "optsize" || strategy == "minsize")
        return optimize("Os");
    else
    {
        assert(false, "unrecognized parameter `" ~ strategy
               ~ "` for `gcc.attribute.optStrategy`");
    }
}

auto optStrategy(A...)(A arguments)
{
    assert(false, "optStrategy attribute argument value is not a string constant");
}

/**
 * When applied to a function, specifies that the function should be optimzed
 * by Graphite, GCC's polyhedral optimizer. Useful for optimizing loops for
 * data locality, vectorization and parallelism.
 *
 * Experimental!
 *
 * Only effective when GDC was built with ISL included.
 */
enum polly = optimize("loop-parallelize-all", "loop-nest-optimize");
