detecting compile-time integer constants with gcc

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

I'm trying to detect compile-time integer constants so I can have some code fail with an error when given an argument that cannot be determined at compile time.  __builtin_constant_p doesn't do what I want because it can return false even when the compiler  can do constant folding with LTO. 

void badArg(const char*) __attribute((error("")));

void foo(int pin)
{
     if (__builtin_constant_p(pin)) {
         if (pin > 1) badArg("pin out of range");
     } else {
         badArg("pin must be a constant");
     }
}

 

if foo is called with a literal, i.e. foo(0), it is OK, and even a const global int.  But calling foo(MYPIN) fails if MYPIN is a non-const global, even when gcc does do constant folding with LTO.

I'd like to be able to work like __builtin_avr_delay_cycles, which fails to compile with "expects a compile time integer constant" only when it is impossible to do constant folding.

 

This appears to be a variation on the static assert problem, but I couldn't figure out a way to do what I want with static_assert.  I did find a couple of github repos with code that looks like it may be able to do what I want, but they seemed to use complex combinations of templates and macros.  I'm looking for a solution that is as readable/maintainable as my example above.

 

I'm fully aware that it's bad coding style to declare global constants without the const keyword (or use a #define or enum instead).  However many of the Arduino official libraries and examples  have spread that infection far and wide...

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

You don't want to use templates?

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

El Tangas wrote:
You don't want to use templates?

 

According to the question, the OP whats to detect what the compiler considers to be a constant, not what the C++ language considers to be a constant. No C++ language feature (templates or whatever) can help will that. Only internal compiler-specific tests, if they exist, are applicable here.

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

So you want it to recognize a constant that only happens to be a constant because of the compiler's analysis says that the value is still the original initialized value, even though it wasn't ever declared to be a constant?

 

int pin=13;
int main() {
  dw_fast(pin, 0);  // Generates fast code
  pin = dynamicFunction();
  dw_fast(pin, 0); // generates compile-time error: "non constant"
}

 

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

westfw wrote:

So you want it to recognize a constant that only happens to be a constant because of the compiler's analysis says that the value is still the original initialized value, even though it wasn't ever declared to be a constant?

 

int pin=13;
int main() {
  dw_fast(pin, 0);  // Generates fast code
  pin = dynamicFunction();
  dw_fast(pin, 0); // generates compile-time error: "non constant"
}

 

Yeah, that's what I'm trying to do.  I already looked at your fastdigitalIO code, but I guess I haven't fully figured it out.

https://github.com/WestfW/Duino-...

 

BTW, would your fastdigitalIO hacks be unnecessary if the digital_pin_xxx_PGM arrays were defined in a .c file with the __flash named address space keyword?

 

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

would your fastdigitalIO hacks be unnecessary if the digital_pin_xxx_PGM arrays were defined in a .c file with the __flash named address space keyword?

Well, there's a bunch of other stuff in digitalWrite() that can be left out of a "fast" version.

The xxx__PGM arrays wouldn't be accessible from C++ (which doesn't support named address spaces), and it was important to me to be able to uses them as they are now.

Note that on the Mega0 series, flash appears in same address space as RAM and the pgmspace code is largely obsolete ( https://github.com/arduino/Ardui... )

There's an open issue on "sneaking" digitalWriteFast() into the 3rd party mega0/xTiny cores, where there is less resistance and less legacy: https://github.com/MCUdude/MegaC...

(But I was sorta dragging my feet till after my daughter's wedding that was supposed to happen this coming weekend, and now I'm dragging my feet because that's not happening :-( )

 

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

ralphd wrote:

westfw wrote:

So you want it to recognize a constant that only happens to be a constant because of the compiler's analysis says that the value is still the original initialized value, even though it wasn't ever declared to be a constant?

 

int pin=13;
int main() {
  dw_fast(pin, 0);  // Generates fast code
  pin = dynamicFunction();
  dw_fast(pin, 0); // generates compile-time error: "non constant"
}

 

Yeah, that's what I'm trying to do.  I already looked at your fastdigitalIO code, but I guess I haven't fully figured it out.

https://github.com/WestfW/Duino-...

 

 

I think my problem was the absence of the always_inline attribute.  I don't really want to always inline larger functions like analogWrite, so I'll make it just an inline wrapper for the implementation.

 

 

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

-flot would seem to allow __builtin_constant_p to do what you want.

That it doesn't suggests you might be out of luck.

Something like this might be useful:

#define safety(pin, pval) do {       \
    if((pval)==pin) foo(pval) ;      \
    else {                         \
        asm(" .error  \"unsafe\""  \
    }                              \
} while(0)

You would need values for pval.

Iluvatar is the better part of Valar.

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

skeeve wrote:

-flot would seem to allow __builtin_constant_p to do what you want.

That it doesn't suggests you might be out of luck.

Something like this might be useful:

#define safety(pin, pval) do {       \
    if((pval)==pin) foo(pval) ;      \
    else {                         \
        asm(" .error  \"unsafe\""  \
    }                              \
} while(0)

You would need values for pval.

 

Nice idea using the assembler message.  When using it as a generic compile-time assert, I haven't figured out a way to get the error output to show the condition that failed.

\AppData\Local\Temp\ccc2RK1P.s: Assembler messages:
\AppData\Local\Temp\cccqoIbe.s:16: Error: invalid arg

Compare that to:

const_assert.c: In function 'main':
const_assert.c:31:5: error: call to 'badArgument' declared with attribute error:

     CHECK_VALUE(LED < 10);

which I get using this code:

void badArgument(const char*) __attribute((error("")));

#define CHECK_VALUE(condition) \
    if (!(condition)) badArgument("")

And I use a separate macro to check for constant arguments:

#define ASSERT_VAR_IS_CONSTANT(pin)               \
    if (!__builtin_constant_p(pin))     \
        badArgument("pin must be a constant")

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

skeeve wrote:

#define safety(pin, pval) do {       \
    if((pval)==pin) foo(pval) ;      \
    else {                         \
        asm(" .error  \"unsafe\""  \
    }                              \
} while(0)

I've come to the conclusion that this will not work with LTO..

The initial compiler output will be assembled.

Replace the asm statement with nonesuch() and you should be good.

My expectation is that undefined external references will be allowed until the final pass.

If a nonesuch is needed, provide one and use avr-nm to look for it in the executable.

Iluvatar is the better part of Valar.