__builtin_constant_p() with const vars vs #defines

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm writing some complex macros that needs to be able to tell if the argument is a constant declared with a #define.

I was using the function:
__builtin_constant_p() but that also returns true if the argument is a constant variable.

For example:

const x = 0;
#define x 0

__builtin_constant_p() will return true for either of these declarations.

Is there a way to tell if an argument is a real constant declared with a #define vs just a constant variable?

I want to have this happen during pre-processing so that I can use the argument as part of another macro that does concatenation.

---- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I want to have this happen during pre-processing

Simply use

#ifdef x
.
.
.

?

Side note: There is no point in the processing of the source where you have

Quote:
#define vs just a constant variable

During preprocessing, the CPP know nothing at all about variables. After preprocessing the compiler proper sees no #defined x at all. All occurences of 'x' has literally been replaced by '0'.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You may find "make file.i" to be very illuminating to see the CPP output.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guys, a .i file will not help in this case.
This isn't a case of not understanding what the pre-processor is generating.

I'm fully aware of how the C preprocessor munches the source for the C compiler.

Perhaps a more full example will better show my question.
The question isn't a simple:
does foo exist as a #define?

but rather is the argument being passed into a macro a #define or a variable.

#define fastWrite(P, V) \
    if (__builtin_constant_p(P) && __builtin_constant_p(V)) {     \
        if(V) CORE_PORTREG(P) |= CORE_BITMASK(P);\
        else CORE_PORTREG(P) &= ~ CORE_BITMASK(P); \
    } else { \
        digitalWrite((P), (V)); \
    }

I need to know if the argument is a #define because CORE_PORTREG() is another complex macro that eventually through concatentation and argument pre-scan will generate the appropriate PORTx register.

This works just fine and generates the proper code when the P argument is a real #define or when the P is a normal variable.
#defines and naked constants use the CORE_PORTREG() macro
and variables use the digitalWrite() call.

The problem is when P is passed something that is declared like this:

const int x = 0;

Then the macro fails because _builtin_constant_p() returns true
and then the macro passes the character "x" to the submacro for concatenation. Which obviously fails because it concatenates the variable name of the P argument into the string.

Since the "x" is being passed to the CORE_PORTREG() macro, it seems to me that
__builtin_constant_p() is being processed by the C preprocessor and not the compiler.

And if it is processed by the c preprocessor, wouldn't it seem more logical if the function only returned true for #defines and not "const" variables as well.

So my question is, is there a way to detect that a macro argument is really a #define constant vs just a variable that has been declared "const"?

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Bill,

I must be misunderstanding something then. Are you suggesting that __builtin_constant_p() runs during pre-processing rather than at run-time then?

I still think you should look at the .i and find out what has actually been generated in place of the macro invocation to understand what's going on. My money is on __builtin_constant_p() still being present AFTER pre-processing.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bperrybap wrote:
Since the "x" is being passed to the CORE_PORTREG() macro, it seems to me that
__builtin_constant_p() is being processed by the C preprocessor and not the compiler.
This conclusion is wrong. Since first the preprocessor does his job, any argument for the fastWrite macro also finds its way to the CORE_PORTREG() macro. The __builtin_constant_p doesn't play any role at that point.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, this:

#include 

void digitalWrite(int n, int m){
}

#define fastWrite(P, V) \ 
    if (__builtin_constant_p(P) && __builtin_constant_p(V)) {     \ 
        if(V) PORTB |= (1<<3);\ 
        else PORTB &= ~ (1<<3); \ 
    } else { \ 
        digitalWrite((P), (V)); \ 
    }

int main(void) {
	fastWrite(3, 7);
	while(1);
}

yields this in the .i:

void digitalWrite(int n, int m){
}
# 14 "../test.c"
int main(void) {
 if (__builtin_constant_p(3) && __builtin_constant_p(7)) { if(7) (*(volatile uint8_t *)((0x18) + 0x20)) |= (1<<3); else (*(volatile uint8_t *)((0x18) + 0x20)) &= ~ (1<<3); } else { digitalWrite((3), (7)); };
 while(1);
}

As predicted the __builtin was NOT evaluated by the CPP. However it is true that the optimiser has "eaten" it:

int main(void) {
	fastWrite(3, 7);
  6e:	c3 9a       	sbi	0x18, 3	; 24
  70:	ff cf       	rjmp	.-2      	; 0x70 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At a guess you want to use this macro to do things like:

PORTB |= (1<<5);
or
PORTB |= (1<<variable);
or
variable |= (1<<5);

You are constructing the PORTB with a "PORT" and "B"
and the mask with "MASK" and "B" and possibly other bits of magic.

At the end of the day, the compiler sees my first expressions (or actually dereferenced memory locations for special function registers)

So the compiler decides what it can and cannot optimise. Providing you have supplied the "const" modifier to a variable, the compiler will know how or if it can optimise.

In the case of avr-gcc, it uses this information pretty well. So the real question is why are you worrying?

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
At a guess you want to use this macro to do things like:

PORTB |= (1<<5);
or
PORTB |= (1<<variable);
or
variable |= (1<<5);

You are constructing the PORTB with a "PORT" and "B"
and the mask with "MASK" and "B" and possibly other bits of magic.

At the end of the day, the compiler sees my first expressions (or actually dereferenced memory locations for special function registers)

So the compiler decides what it can and cannot optimize. Providing you have supplied the "const" modifier to a variable, the compiler will know how or if it can optimize.

In the case of avr-gcc, it uses this information pretty well. So the real question is why are you worrying?

David.

That is not quite where I was trying to go.

Background:
Obviously you can tell this is Arduino based.
I really don't like arduino or want to have anything to do with it,
but I'm caught up in the middle of trying to make their API really "work", which today it does not.
(Today, it corrupts register bits when interrupt functions use their API)

Quote:
NOTE: While my personal preference is against the arduino wiring API, I do not what this thread to degrade into a arduino API flame war. I'm merely inserting some color/backround here for folks.

I did go back and look at the .i file and yes I was mistaken on CPPs handling of this.
Not sure what I was thinking as I've looked at the .i output many times.
(I did think it was odd that CPP could process such information).

So while yes the #define case does eventually need to boil down to something like:

PORTx |= MASK;

The variable case does not boil down to

var |= MASK;

In their environment it calls a function and then boils down to

*regaddr |= MASK;

The Arduino API jumps through hoops and does flash table lookups to map a pin# to a port memory address and bit mask.
And that is issue they are facing.
Not only is it very slow,
but when the port is referenced through
a variable address pointer, then there is no way to optimize the code for CBI/SBI instructions.

This creates problems as the operation is no longer atomic.

(Yes I know that is also fixable, but not when the Arduino guys in charge don't see it as an issue)

What I was looking for was a quick an dirty way to create a wrapper macro that could
do some CPP magic to create different code
based on the argument type passed into macro.

So it at least it would work properly (and be much faster) when the arguments were #define constants and then revert back to the original code (which still has issues) for variables.

While I can do it with a long string of ternary operations (which will work for both #defines and const int variables) I was trying to "cheat" and use some concatenation magic to do some expansions when the values were #defines.

After looking at it closer, I don't think it can be done at the CPP level via a simple wrapper macro and must be done in code, which pushes things back to either inline functions, case statements, or ternary operations.

Which I've also done but are kind of a PITA.

---

Trying to make the Arduino API really work without fixing the underlying code or modifying the API itself is difficult if not impossible. Making it even tougher is that people are starting to adopt the practice of using:

const int foo = value;

Interchangeably with

#define foo value

and expect the code to behave and work the same way, and also want the generated code to be as small and tight as possible.
i.e. single instruction CBI/SBI when possible.

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bperrybap wrote:
I'm writing some complex macros that needs to be able to tell if the argument is a constant declared with a #define.

I was using the function:
__builtin_constant_p() but that also returns true if the argument is a constant variable.

For example:

const x = 0;
#define x 0

__builtin_constant_p() will return true for either of these declarations.

Is there a way to tell if an argument is a real constant declared with a #define vs just a constant variable?

You might not need to.
In both those cases, the type of x is int.
gcc will let you test types.
If x is a constant and int, you can just do the math.
Quote:
I want to have this happen during pre-processing so that I can use the argument as part of another macro that does concatenation.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
bperrybap wrote:
I'm writing some complex macros that needs to be able to tell if the argument is a constant declared with a #define.

I was using the function:
__builtin_constant_p() but that also returns true if the argument is a constant variable.

For example:

const x = 0;
#define x 0

__builtin_constant_p() will return true for either of these declarations.

Is there a way to tell if an argument is a real constant declared with a #define vs just a constant variable?

You might not need to.
In both those cases, the type of x is int.
gcc will let you test types.
If x is a constant and int, you can just do the math.

Well the problem is, what needs to be done isn't really "math"
it is essentially a lookup. You have to convert from essentially an arbitrary number to the AVR port address. And this isn't just a calculation.
And keep in mind that the number to port mappings change for each AVR.

But like I said, I've done it with if/else,
comparisons, case statements, and ternary operators using the compiler. I had hopes of being able to do something with the pre-processor to avoid that but I don't think it is possible.

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You want to do things like:

#define _PINBIT0 0
#define _PINBIT1 3
  :
#define _PINBIT12 4

#define _PINPORT0 PORTB
#define _PINPORT1 PORTB
  :
#define _PINPORT12 PORTE

#define CONCAT(a, b) a ## b

#define SETBIT(pinno) sbi(CONCAT(_PINPORT,pinno), CONCAT(_PINBIT,pinno))

Except that this only works with "real" constants, and you need to test for real constant-ness so that you can do something different for SETBIT(pinvariable)

Right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bperrybap wrote:
skeeve wrote:
bperrybap wrote:
I'm writing some complex macros that needs to be able to tell if the argument is a constant declared with a #define.

I was using the function:
__builtin_constant_p() but that also returns true if the argument is a constant variable.

For example:

const x = 0;
#define x 0

__builtin_constant_p() will return true for either of these declarations.

Is there a way to tell if an argument is a real constant declared with a #define vs just a constant variable?

You might not need to.
In both those cases, the type of x is int.
gcc will let you test types.
If x is a constant and int, you can just do the math.

Well the problem is, what needs to be done isn't really "math"
it is essentially a lookup. You have to convert from essentially an arbitrary number to the AVR port address. And this isn't just a calculation.
And keep in mind that the number to port mappings change for each AVR.

But like I said, I've done it with if/else,
comparisons, case statements, and ternary operators using the compiler. I had hopes of being able to do something with the pre-processor to avoid that but I don't think it is possible.

What happens if you use only constant
indices on a static const table?
The optimizer has enough information to get rid of the table.

Iluvatar is the better part of Valar.