Messages in this thread | | | Subject | Re: detecting integer constant expressions in macros | From | Rasmus Villemoes <> | Date | Wed, 21 Mar 2018 22:10:31 +0100 |
| |
On 2018-03-21 00:08, Linus Torvalds wrote: > On Tue, Mar 20, 2018 at 3:13 PM, Uecker, Martin > <Martin.Uecker@med.uni-goettingen.de> wrote: >> >> here is an idea: > > That's not "an idea". > > That is either genius, or a seriously diseased mind. > > I can't quite tell which. > >> a test for integer constant expressions which returns an >> integer constant expression itself which should be suitable >> for passing to __builtin_choose_expr might be: >> >> #define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
Wow. This is absolutely awesome.
>> This also does not evaluate x itself on gcc although this is >> not guaranteed by the standard. (And I haven't tried any older >> gcc.) > > Oh, I think it's guaranteed by the standard that 'sizeof()' doesn't > evaluate the argument value, only the type.
Even if there's some hypothetical scenario where x ends up containing something of variably-modified type, could one just use 0? instead of 1? . That doesn't affect the type of the whole expression.
> I'm in awe of your truly marvelously disgusting hack. That is truly a > work of art.
I'm not really worthy of commenting or trying to improve on this, but I think it's important that 'git blame' will forever give Martin the credit for this, so two minor suggestions:
- Cast (x) to (long) to allow x to have type u64 on a 32-bit platform without giving "warning: cast to pointer from integer of different size". That also allows x to have pointer type (it's theoretically useful to take max() of two pointers into an array), and not so important for the kernel, floating point type.
- Maybe use (int*)4 to avoid the compiler complaining about creating a non-aligned pointer.
> I'm sure it doesn't work or causes warnings for various reasons, but > it's still a thing of beaty.
I tried putting the below ugly test code through various gcc versions. In no cases could I get different optimization options to generate different results. I also couldn't get -Wall -Wextra to produce any warnings that wasn't just due to the silly input (i.e., yes, I know I have a comma expression with no side effects....). Most importantly, ICE_P was always accepted as an ICE itself. Everything from 4.6 onwards gives the same and expected output, namely 1 in the first four lines, 0 otherwise. gcc 4.4 instead produces this:
4 1 sizeof(long) 1 5ull - 3u 1 3.2 1 square(2) 0 square(argc) 0 squarec(2) 1 squarec(argc) 1 1+argc-argc 1 1+argc+argc+1-argc-argc 1 bignum() - 1 0 0*bignum() 0 0*bignumc() 1 sizeof(foo) 0 p 1 p < q 1 p++ 0 main 1 malloc(8) 0 v = malloc(8) 0 v 1 x++ 0 y++ 0 (3, 2, 1) 0 ({x++; 0; }) 0 ({square(y--); 0; }) 0 (square(x), 3) 0 (squarec(x), 3) 0 ({squarec(x); 3;}) 0 ({squarec(x);}) 1
So it seems to accept/treat (x)*0l as NULL as long as x has no side-effects - where a function call is treated as having side effects unless the function is declared with the const attribute, comma-expressions are hard-coded as having side-effects [and what else would they be good for, so that's not totally unreasonable...], and statement expressions have side effects if they have more than one statement.
Rasmus
#include <stdio.h> #include <stdlib.h>
static inline __attribute__((__const__)) unsigned squarec(unsigned n) { return n*n; }
static inline unsigned square(unsigned n) { return n*n; }
static inline unsigned long long bignum(void) { return 1000000000000ULL; }
static inline __attribute__((__const__)) unsigned long long bignumc(void) { return 1000000000000ULL; }
/* #define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((long)(x) * 0l)) : (int*)1))) */ #define ICE_P(x) (__builtin_types_compatible_p(typeof(0 ? ((void*)((long)(x) * 0l)) : (int*)1), int*))
#define t(x) printf("%-20s\t%d\n", #x, __builtin_choose_expr(ICE_P(x), 1, 0))
int main(int argc, char *argv[]) { char foo[argc++]; char **p, **q; int x = 5, y = 8; void *v; p = &argv[3]; q = &argv[6]; t(4); t(sizeof(long)); t(5ull - 3u); t(3.2); t(square(2)); t(square(argc)); t(squarec(2)); t(squarec(argc)); t(1+argc-argc); t(1+argc+argc+1-argc-argc); t(bignum() - 1); t(0*bignum()); t(0*bignumc()); t(sizeof(foo)); t(p); t(p < q); t(p++); t(main); t(malloc(8)); t(v = malloc(8)); t(v); t(x++); t(y++); t((3, 2, 1)); t(({x++; 0; })); t(({square(y--); 0; })); t((square(x), 3)); t((squarec(x), 3)); t(({squarec(x); 3;})); t(({squarec(x);}));
/* Shutup distracting warnings. */ if (argc > 100000) { printf("%llu", square(argc) + squarec(argc) + bignum() + bignumc()); } return 0; }
| |