http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46711
Summary: __builtin_choose_expr checks not chosen expression Product: gcc Version: 4.4.5 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c AssignedTo: unassig...@gcc.gnu.org ReportedBy: prze...@gmail.com Created attachment 22569 --> http://gcc.gnu.org/bugzilla/attachment.cgi?id=22569 Test case. Built-in Function: type __builtin_choose_expr (const_exp, exp1, exp2) Excerpts from doc: This built-in function is analogous to the `? :' operator in C, except that the expression returned has its type unaltered by promotion rules. Also, the built-in function does not evaluate the expression that was not chosen. Note: This construct is only available for C. Furthermore, the unused expression (exp1 or exp2 depending on the value of const_exp) may still generate syntax errors. This may change in future revisions. -- http://gcc.gnu.org/onlinedocs/gcc-4.5.1/gcc/Other-Builtins.html #include <stdio.h> int main() { int a = 0; printf("a is %s\n", __builtin_choose_expr(__builtin_constant_p(a), __builtin_choose_expr((a) == 0, "constant 0", __builtin_choose_expr((a) == 1, "constant 1", "constant non-{0,1}" ) ), "non-constant" ) ); return 0; } (attached also as a file) __builtin_choose_expr.c: In function ‘main’: __builtin_choose_expr.c:11: error: first argument to ‘__builtin_choose_expr’ not a constant __builtin_choose_expr.c:9: error: first argument to ‘__builtin_choose_expr’ not a constant It's theoretically correct according to the note mentioned before, but it rather should compile without errors. [ BTW when you run gcc with optimization, i.e. -On, where n > 0, it shows also a bug 19449: __builtin_choose_expr.c:8: error: first argument to ‘__builtin_choose_expr’ not a constant ] Expressions are checked in bottom-up order, which is fine, but only for const_exp part. exp1 or exp2 shall be checked only if const_exp is true or false respectively, avoiding the not chosen expression. Example I gave you above is simple and imaginary, but it shows that __builtin_choose_expr combined with other builtins (such as __builtin_constant_p) can be useful. Moreover, as it can return lvalue, it's cannot be easily replaced without introducing function with ifs or switch and returning pointer to some type. Better example (linux kernel-space): #ifdef CONFIG_X86_64 # define PARAM0(regs) (regs)->di # define PARAM1(regs) (regs)->si # define PARAM2(regs) (regs)->dx # define PARAM3(regs) (regs)->r10 # define PARAM4(regs) (regs)->r8 # define PARAM5(regs) (regs)->r9 static inline unsigned long * _param(struct pt_regs *regs, int num) { switch (num) { case 0: return &PARAM0(regs); case 1: return &PARAM1(regs); case 2: return &PARAM2(regs); case 3: return &PARAM3(regs); case 4: return &PARAM4(regs); case 5: return &PARAM5(regs); } /* should not happen */ return NULL; } # define PARAM(regs, num) \ __builtin_choose_expr(__builtin_constant_p((num)), \ __builtin_choose_expr((num) == 0, PARAM0(regs), \ __builtin_choose_expr((num) == 1, PARAM1(regs), \ __builtin_choose_expr((num) == 2, PARAM2(regs), \ __builtin_choose_expr((num) == 3, PARAM3(regs), \ __builtin_choose_expr((num) == 4, PARAM4(regs), \ __builtin_choose_expr((num) == 5, PARAM5(regs), 0)))))), \ *_param(regs, num) ) #endif PARAM can be even simplified: # define PARAM(regs, num) \ __builtin_choose_expr(__builtin_constant_p(num), \ __builtin_choose_expr((num) < 6, \ PARAM##num(regs), \ 0 \ ), \ *_param(regs, num)) \ ) In either case it unfortunately doesn't work now. Theoretically I can use the function directly, but it isn't inlined with known result for constant expressions.