Incongruency in __builtin_constant_p with pointer argument

$ cat t.c
typedef const int * ptr;

const int i = 0;
const ptr p = &i;

int a[__builtin_constant_p(i) ? 1 : -1];
int b[__builtin_constant_p(p) ? 1 : -1];

$ clang -S t.c
t.c:7:7: error: 'b' declared as an array with a negative size
int b[__builtin_constant_p(p) ? 1 : -1];
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
$ gcc -S t.c
t.c:6: error: size of array ‘a’ is negative
t.c:7: error: size of array ‘b’ is negative

I'm perfectly fine with the fact that clang treats const typed variable
as evaluatable constant (although gcc don't), but this should be the
same for both cases.

The fix is trivial and I'll commit it myself, but before I'd like to
know if it is preferred that __builtin_constant_p returns true for both
or false for both.

But a pointer to a global variable is not known at compile-time.
At such the behavior is completely correct and valid.

Joerg

I think that clang is right to consider pointer to static memory region
to be constant at compile-time and hence candidate to constant-folding.
(note that the documentation does not say "constant with a known value
at compile-time", but "value known to be constant at compile time").

The following snippet shows another example of what clang does:

$ cat t.c
int i;

int a[__builtin_constant_p(&i) ? 1 : -1];
$ clang -S t.c
$

The point is only if this constant folding should be reported by
__builtin_constant_p or if the intention is that clang implementation
should mimic gcc behaviour.

Pointers are only semi-constant. You can consider them as constants for
a few purposes (e.g. adding or substracting them), but anything else
generally doesn't work due to the limitations of the relocation model.
As such, it doesn't make sense to me to return True since they can't be
fully used with constant-folding.

Joerg

Abramo Bagnara <abramo.bagnara@gmail.com>
writes:

I think that clang is right to consider pointer to static memory region
to be constant at compile-time and hence candidate to constant-folding.
(note that the documentation does not say "constant with a known value
at compile-time", but "value known to be constant at compile time").

Whatever wording is in the documentation, I think people tend to _use_
__builtin_constant_p as if it means "the value is known to the
compiler/optimizer, and so most of the calculation can be done at
compile-time." A "constant" known only at link-time would break this
assumption.

So if consistency is necessary (I dunno), it seems better to follow gcc
and declare them both non-__builtin_constant_p.

-Miles

Abramo Bagnara <abramo.bagnara@gmail.com>
writes:

I think that clang is right to consider pointer to static memory region
to be constant at compile-time and hence candidate to constant-folding.
(note that the documentation does not say "constant with a known value
at compile-time", but "value known to be constant at compile time").

Whatever wording is in the documentation, I think people tend to _use_
__builtin_constant_p as if it means "the value is known to the
compiler/optimizer, and so most of the calculation can be done at
compile-time." A "constant" known only at link-time would break this
assumption.

So if consistency is necessary (I dunno), it seems better to follow gcc
and declare them both non-__builtin_constant_p.

-Miles

The documentation of gcc (4.5.1) says:

A return of 0 does not indicate that the value is not a constant,
but merely that GCC cannot prove it is a constant
with the specified value of the -O option.

Hence, the behavior changes depending on the use of -O.
If you use -O1 or above, gcc will return 1 on those pointers too.

clang/llvm seems to be a bit inconsistent in this respect.
If you compile the following program with -O:

No, it isn't. Just because it doesn't know the value of a pointer
doesn't mean that the value pointed to is unknown.

Joerg

Some user input: when I've used __builtin_constant_p(), the intention has been to predict whether feeding the specified value into some block of code (for instance, a large switch statement) is likely to be optimized to a constant or a subset of the code.

As such, I only want to know whether the value is a constant known to this compiler at this time; whether it's a literal, a constant expression, or another type of const-foldable value, or whether there exist particular operations that will produce constants from it. This is how I've always interpreted the gcc manual. I don't see how I could possibly benefit from it lying to me for "consistency".

As for the "inconsistency" involving strlen(), both gcc and clang seem to give useful results for __builtin_constant_p(strlen(foo)).

Hello,

From http://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Other-Builtins.html:

You can use the built-in function __builtin_constant_p to determine
if a value is known to be constant at compile-time and hence that GCC
can perform constant-folding on expressions involving that value. The
argument of the function is the value to test. The function returns
the integer 1 if the argument is known to be a compile-time constant
and 0 if it is not known to be a compile-time constant. A return of 0
does not indicate that the value is not a constant, but merely that
GCC cannot prove it is a constant with the specified value of the -O
option.

I think that clang is right to consider pointer to static memory region
to be constant at compile-time and hence candidate to constant-folding.
(note that the documentation does not say "constant with a known value
at compile-time", but "value known to be constant at compile time").

But "candidate to constant-folding" requires the latter, I think. E.g. "a + 2" cannot be
folded if the value of "a" is not known at compile-time, even if it happens to be constant.

Jonathan

$ cat t.c
int i;

int a[__builtin_constant_p(&i+1) ? 1 : -1];
$ clang -S t.c
$

clang disagrees...

What should be taken in account is that only the "numeric" value of the
pointer is not known at compile time, but its "symbolic" value is known.

Ping.

We have also other fixes for clang constant expr evaluator, but we need
to know the wanted policy. Currently it is not clear what is wishable
and implementation is often incongruent.

Also it need to be decided if __builtin_constant_p is tied to constant
expr evaluator or not, i.e. if the value returned by this builtin should
be the same of constant expr evaluator once taken in account the side
effects.

Have you read http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins
? You're trying to impose semantics on __builtin_constant_p that
aren't part of its definition. Currently, the results just sort of
fall out of whatever the AST-level constant folder happens to know how
to fold. That isn't a guarantee, though; see also
http://llvm.org/bugs/show_bug.cgi?id=4898.

-Eli

$ cat t.c
typedef const int * ptr;

const int i = 0;
const ptr p = &i;

int a[__builtin_constant_p(i) ? 1 : -1];
int b[__builtin_constant_p(p) ? 1 : -1];

$ clang -S t.c
t.c:7:7: error: 'b' declared as an array with a negative size
int b[__builtin_constant_p(p) ? 1 : -1];
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
$ gcc -S t.c
t.c:6: error: size of array ‘a’ is negative
t.c:7: error: size of array ‘b’ is negative

I'm perfectly fine with the fact that clang treats const typed variable
as evaluatable constant (although gcc don't), but this should be the
same for both cases.

The fix is trivial and I'll commit it myself, but before I'd like to
know if it is preferred that __builtin_constant_p returns true for both
or false for both.

Ping.

We have also other fixes for clang constant expr evaluator, but we need
to know the wanted policy. Currently it is not clear what is wishable
and implementation is often incongruent.

Also it need to be decided if __builtin_constant_p is tied to constant
expr evaluator or not, i.e. if the value returned by this builtin should
be the same of constant expr evaluator once taken in account the side
effects.

Have you read http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins
?

Yes, of course.

  You're trying to impose semantics on __builtin_constant_p that

aren't part of its definition. Currently, the results just sort of
fall out of whatever the AST-level constant folder happens to know how
to fold. That isn't a guarantee, though; see also
http://llvm.org/bugs/show_bug.cgi?id=4898.

Note that I'm not currently trying to impose any semantics, just to make
implementation congruent with wished policy.
To do this I need to know it, whatever it is :wink:

Take this example:

$ cat t.c

const int a = 0;
const char * const b = "p";

char w[__builtin_constant_p(a) ? 1 : -1];
char x[__builtin_constant_p(b) ? 1 : -1];
char y[__builtin_constant_p("p") ? 1 : -1];
char z[__builtin_constant_p(&a) ? 1 : -1];
abramo@igor:~$ clang -S t.c
t.c:6:8: error: 'x' declared as an array with a negative size
char x[__builtin_constant_p(b) ? 1 : -1];
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.c:7:8: error: 'y' declared as an array with a negative size
char y[__builtin_constant_p("p") ? 1 : -1];
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

The 'w' line show that a constant variabile is treated as a foldable
constant, but the 'x' line contraddicts that.

One may think that pointer values are never treated as foldable
constants, but then 'z' line is not explainable.

Once decided which is the policy, to fix all that is easy, but now
things are somewhat confused.

For stuff inside static intializers in particular, we should probably
try to be consistent with gcc, I guess. Granted, there isn't much
code that does things like that...

-Eli