GCC Common-Function-Attributes/const

2018-11-26 Thread cmdLP #CODE
Dear GCC-developer team,

The specification of the const-attribute is a bit ambiguous, it does not
fully specify which global variables are allowed to be read from. Obviously
constant global compile-time initialized variables can be accessed without
problem. But what about run time initialized variables like a lookup table
or the initialization of an api? Does GCC may invoke the function before
the initialization of the lookup table/api; or is it guaranteed, that the
function is only called in places, where it would be called without the
attribute. I also propose some additional attributes to let the programmer
clarify his/her intentions.

Here are some code snippets, how GCC might change the generated code,
according to the documentation.
In my opinion, each generated output should be assigned to a distinct
attribute.

*Function declarations:*
*void init_values();*
*int read_value(int index) [[gnu::const]];*

*void use_value(int value);*

*Problematic code:*
*int main(int argc, char** argv) {*
*init_values();*
*for(int i=1; i < argc; ++i) {*
*int value = read_value(0); // constant*
*use_value(value, argv[i]);*
*}*
*}*

Here are some possible outputs, which are possible, because of the
ambiguity of the documentation. The attribute next to "Transformation" is
the proposed new attribute names, which could lead to the transformatio
(when [[gnu::const]] is replaced with it).
*Transformation [[gnu::const, gnu::is_simple]]*
*int read_value__with_0;*

*int main(int argc, char**) {*
*read_value__with_0 = read_value(0);*
*init_values();*
*for(int i=1; i < argc; ++i) {*
*use_value(read_value__with_0, argv[i]);*
*}*
*}*

The code is called at some undefined point, maybe at the start of main,
even if it is never used, because the compiler assumes, that the function
simply reads from constant memory/does a simple calculation. In the case of
the example, it is not what the programmer intended.

*Transformation [[gnu::const]]*
*int read_value__with_0;*

*int main(int argc, char** argv) {*
*if(argc > 1) read_value__with_0 = read_value(0);*
*init_values();*
*for(int i=1; i < argc; ++i) {*
*use_value(read_value__with_0, argv[i]);*
*}*
*}*

This is almost the same to the previous version, but it only calls the
function, if the result is used.

Both attributes (the time when it is called is undefined) can also result
in the following code where the attributes give a stronger guarantee, when
it is called (the first time). The following works semantically as the
programmer intended/when the attributes were ignored.

*Transformation** [[gnu::const(init_values()), gnu::is_simple]]*
*int read_value__with_0;*
*int main() {*
*init_values();*
*read_value__with_0 = read_value(0);*
*for(int i=1; i < 100; ++i) {*
*use_value(read_value__with_0, argv[i]);*
*}*
*}*

*Transformation **[[gnu::const(init_values())]]*
*int read_value__with_0;*
*int main() {*
*init_values();*
*if(argc > 1) read_value__with_0 = read_value(0);*
*for(int i=1; i < 100; ++i) {*
*use_value(read_value__with_0, argv[i]);*
*}*
*}*


The function is guaranteed to never use the returned values again, when the
given expression in the attribute parameter list is called, so the compiler
interprets the given function as an initializing function.


*PROPOSED ATTRIBUTES*

*[[gnu::is_simple]]*
In connection to the [[gnu::const]] attribute this attribute implies, that
the function might be called when the result is not used. Eg. when the
function is called in a conditional branch, the call can be extracted from
the branch and can be called outside. This should be applied to very simple
functions.

*[[gnu::const]]*
The meaning is kept as before. But the function can be called any time.
This enshures, that no expression is called inside the function, which has
an observable effect. But when the function is used in a
[[gnu::const(...)]] annotated function each call to this function is
considered an observable effect to the function.


*[[gnu::const([():],...)]]*
This is an extension to the old [[gnu::const]] attribute. But: When some of
the expressions is called, the previous return values are now invalid (the
function depends on the expression). The parameter list specifies variables
used in the expression, which are not known. In the expression, the
parameter of the annotated function itself, member variables/functions and
static variables/functions can be used. If [[gnu::const]] is used on non
static member functions, the function automaticly depends on the
constructor.
Destructors automaticly depend on all non static member functions.

Unclear: A function should never depend on a function/expression, which
calls the annotated function.


*[[gnu::calls([():],...)]]*
This function attributes tells the compiler, that the given expression
might be called inside. This makes older values recieved from
[[gnu::const(...)]] functions (which depend on (parts of) the 

__attribute__((early_branch))

2019-04-30 Thread cmdLP #CODE
Hello GCC-team,

I use GCC for all my C and C++ programs. I know how to use GCC, but I am
not a contributor to GCC (yet). I often discover some problems C and C++
code have in general. There is often the choice between fast or readable
code. Some code I have seen performs well but looks ugly (goto, etc.);
other code is simple, but has some performance problems. What if we could
make the simple code perform well?

There is a common problem with conditional branches inside loops. This can
decrease the performance of a program. To fix this issue, the conditional
branch should be moved outside of the loop. Sometimes this optimization is
done by the compiler, but guessing on optimizations done by the compiler is
really bad. Often it is not easy to transform the source code to have the
conditional branching outside the loop. Instead I propose a new attribute,
which forces the compiler to do a conditional branch (based on the
annotated parameter) at the beginning of a function. It branches to the
corresponding code of the function compiled with the value being constant.

Here is a code example, which contains this issue.

enum reduce_op
{
REDUCE_ADD,
REDUCE_MULT,
REDUCE_MIN,
REDUCE_MAX
};

/* performance critical function */
void reduce_data(enum reduce_op reduce,
 unsigned const* data,
 unsigned data_size)
{
unsigned i, result, item;

result = reduce == REDUCE_MULT ?  1u
   : reduce == REDUCE_MIN  ? ~0u // ~0u is UINT_MAX
   :  0u;

for(i = 0; i < data_size; ++i)
{
item = data[i];

switch(reduce)
{
case REDUCE_ADD:
result += item;
break;

case REDUCE_MULT:
result *= item;
break;

case REDUCE_MIN:
if(item < result) result = item;
// RIP: result  result) result = item;
// RIP: result >?= item;
break;
}
}

return result;
}

The value of  reduce  does not change inside the function. For this
example, the optimization is trivial. But consider more complex examples.
The function should be optimized to:

void reduce_data(enum reduce_op reduce,
 unsigned const* data,
 unsigned data_size)
{
unsigned i, result, item;

switch(reduce)
{
case REDUCE_ADD:
result = 0;

for(i = 0; i < data_size; ++i)
result += data[i];

return result;

case REDUCE_MULT:
result = 1;

for(i = 0; i < data_size; ++i)
result *= data[i];

return result;

case REDUCE_MIN:
result = ~0u;

for(i = 0; i < data_size; ++i)
{
item = data[i];
if(item < result) result = item;
}

return result;

case REDUCE_MAX:
result = 0;

for(i = 0; i < data_size; ++i)
{
item = data[i];
if(item > result) result = item;
}

return result;

default: return 0u;
}
}

As I have mentioned, the value is treated as constant in the code. The
compiler simply compiles the body of the function for each enum-value as if
it was constant and puts it in a conditional branch. This means that
__builtin_constant_p evaluates to true/1, when the parameter has the
attribute. But duplicate code should also be avoided, the initialization of
the loop counter  i  could be moved before the branch.

You might ask, what about values which are not declared in the enum? The
compiler simply compiles a default case as in the example code above. The
default case equals to the normal code (as if the variable was not
annotated with the attribute), except that the compiler knows, that the
enum value is never a value declared in the enum. In this case the return
value is always 0. In the default case __builtin_constant_p with the
annotated variable evaluates to false. (But as  reduce == REDUCE_ADD  is
always false  __builtin_constant_p(reduce == REDUCE_ADD)  should be always
true.)

The attribute can only be applied in the implementation of a function. I
call the attribute early_branch. The usage of the attribute could be

void reduce_data(enum reduce_op reduce __attribute__((early_branch)),
 unsigned const* data,
 unsigned data_size)
{
...
}


The way to remove the default case could be to just test against non-enum
values and insert __builtin_unreachable()

if(reduce != REDUCE_ADD  &&
   reduce != REDUC