Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re^2: [OT] Abusing the C preprocessor

by afoken (Chancellor)
on Nov 11, 2017 at 20:15 UTC ( [id://1203194]=note: print w/replies, xml ) Need Help??


in reply to Re: [OT] Abusing the C preprocessor
in thread [OT] Abusing the C preprocessor

#define CFG_USED(type, name, stuff) \ { .compFunc = compare_ ## type; /* */ }, #define CFG_UNUSED(type, name, stuff) /* unused */ #define CFG_ADD(type, name, stuff) CFG_USE_ ## type (type,name,stuff) #ifdef USING_int #define CFG_USE_int CFG_USED #else #define CFG_USE_int CFG_UNUSED #endif

I don't get it. To me, this looks backwards. I don't wan't to define USING_int. I want the precompiler to "know" if the configuration file contains one or more CFG_ADD(int, ...) or not. Building on that "knowledge", the precompiler should either emit the function definition for compare_int() or not.

You could define the compare functions via inline versions. [...] There should be no warnings for unused inline functions.

Nice idea. I suspected that CC would not accept inline functions for function pointer, but my Linux gcc 5.x does:

/tmp>cat testing.c #include <stdio.h> #include <stdlib.h> static inline int compare_short(const void * a, const void * b) { if (*(short *)a < *(short *)b) return -1; if (*(short *)a > *(short *)b) return 1; return 0; } static inline int compare_int(const void * a, const void * b) { if (*(int *)a < *(int *)b) return -1; if (*(int *)a > *(int *)b) return 1; return 0; } static int foo[5]={47,11,0,8,15}; int main(int argc, char ** argv) { qsort(foo,sizeof(foo)/sizeof(foo[0]),sizeof(foo[0]),&compare_i +nt); } /tmp>CFLAGS="-Wall -pedantic" make testing cc -Wall -pedantic testing.c -o testing /tmp>cc --version cc (GCC) 5.3.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There i +s NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR +POSE. /tmp>

And so does the gcc 4.x for my target processor. COOL!

After that, you might use the same configure mechanism to conditionally generate function definitions:

static foo compare_foo(void *a, void *b) { return _inline_cmpfunc_foo( +a, b); }

That function does not make sense in this form. Compare functions are expected to return int, and I guess that you wanted to call _inine_cmpfunc_int(a,b). So I assume you meant to wrote something like this:

#define CFG_ADD(TYPE,NAME,STUFF) \ static int compare_ ## NAME(const void * a, const void * b) \ { \ return _inline_cmpfunc_ ## TYPE(a,b); \ }

This is great - except that now, the compiler may legally generate one compare function per NAME. Assuming a configuration file with 10 ints and 10 shorts, the compiler could emit 20 compare functions, 10 for int and 10 for short, and all would be in use. That's even worse than the current situation: Two compare functions for int and short, and eight unused functions for other types.

I think gcc should be able to optimize and ignore the inline. But then, gcc should also be able to optimize the eight unused functions away. Does it optimize in that way? And will it still optimize that way when we update to a gcc version three major version numbers away from now?

And here we are at a point that I did not mention. Having to answer "It depends" to a yes-or-no question is not acceptable in that project. The system will run in a highly regulated environment, and we need predictable behaviour.

Regarding warnings: you can suppress unused warnings on individual basis with an __attribute__((__unused__)).

Right. But the warning is not just cosmetic in this case. The compiler generates code that should not be generated. I could not care less if the target system was a PC from this century. But my target system is an embedded microcontroller with limited program memory.

Running some primitive tests, it seems that gcc not only warns about unused functions, but actually removes them, even when optimizing is disabled (-O0).

I think this is the way to go. I'll wrap __attribute__((__unused__)) in a macro named MAYBE_UNUSED (or similar), and at a lot of documentation explaining why and how this works together with gcc. The nice thing about this solution is that it is testable. We can simply look into the map file and search for the name of the functions that should not be there.

THANKS!

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Replies are listed 'Best First'.
Re^3: [OT] Abusing the C preprocessor
by Anonymous Monk on Nov 12, 2017 at 16:43 UTC

    But then, gcc should also be able to optimize the eight unused functions away. Does it optimize in that way? And will it still optimize that way when we update to a gcc version three major version numbers away from now?
    Functions declared with static have "internal linkage". When unused (have no direct calls nor reference taken), the compiler can eliminate those functions as dead code. Depending on this optimization is quite reasonable.

    I'd wholeheartedly suggest exploring the effect of different options on generated code and looking at the assembly in general. Gcc with -O1 or -Os is usually okay, except gcc -Os does not optimize constant divisions via muls (e.g. x = x / 10).

    Keeping a basic tally of object sizes is also advisable. I've used makefile rules to objdump -t foo.o > foo.syms and a simple symcmp.pl to report a sorted diff between object directories so that any big regressions with a new compiler will stick out in the noise.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1203194]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (8)
As of 2024-04-25 11:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found