Well, for the first error you should have to post the exact argument passed to the macro at invocation, as probably you have passed a variable name as parameter and that requires code (executable) to retrieve the variable value and put it on the right place (this is what is not constant
). By expanding the macros as used (you don't post any actual macro expansion, only the macro definitions) the expansion of LS_BL2_EL3_RW_MEM
gives:
MAP_REGION_FLAT(BL2_EL3_RW_START, \
BL2_EL3_RW_SIZE, \
MT_DEVICE | MT_RW | MT_SECURE)
where the second parameter is a macro invocation that expands to
(const unsigned long) ((&__RW_END__) - (&__RW_START__))
which is not a constant expression, as it involves the evaluation of two long
variable addresses (namely __RW_END__
and __RW_START__
) and compute their difference (as a C expression). The C language only considers the &
address operator as being able to construct a constant expression when the variable is global (as this is the case) as the addresses of dynamic variables and pointer dereferences are dynamic and non constant in nature. The problem here is that you have to substract the addresses of the two variables to form the initialization value, and that requires code (so no data inline can be used to initialize, only the calculation proper) cannot initialise a struct
field with a non-constant expression. You have to use an assignment for that.
Even if having the cast to (const unsigned long)
, that doesn't imply the expression is a constant expression. What the const
means here is that herein on, the expression value cannot be further modified, it should be considered a constant.... but that doesn't mean you are parsing a constant expression. A constant expression is one that can be solved at compile time (and indeed, an expression that depends on the value of two linker variables, cannot be determined at compile time, you'll agree with me on this, for sure) And in the present case, it is indeed true (that the expression is not constant) for the addresses of those variables, as the position of the two variables is not known until the linker has placed all segments inline and determines (in the second pass) the offset values and addresses of everything.
Further, even the value of __RW_SIZE__
(which is the thing computed in the macro) is not a constant expression. It is computed at link time, and so you cannot determine the result value until you have finished compiling. The macro purpose is only for the case you don't have access to the variable.
Finally, the last case (the compiler not giving a complaint when the value used is the address of the __RW_SIZE__
variable) is that then, the expression is a constant... the constant is precisely the address of a global variable. This is determined at link time, but with a constant, fixed value, that has not to be calculated. But you have to think on one thing... the value of __RW_SIZE__
is the value you need, and not it's address, so if you initialise the struct field with the address of __RW_SIZE__
, then you are making a mistake, as the address and the value of __RW_SIZE__
are two different things (indeed, if you substitute the value of __RW_SIZE__
into the initializer, you'll see the same error as in the first case, because the contents of __RW_SIZE__
are not know at compilation time.)
WHAT THE HELL IS A CONSTANT EXPRESSION
Constant expression (compile time) is an expression that can fully be computed at compile time (not something labeled with a const
keyword) The compiler allows you to put an expression to facilitate things, but the compilation of such an initializer is always a constan value. This means that, if you put something like:
int a = 3 + 2;
the compiler will reserve space for a variable and fill the .data
segment with four bytes and the constant 5
on it (the compiler calculates the value, instead of generating code to calculate it)
This makes things like
#define RAD_TO_DEGREES (180.0 / MATH_PI)
to be possible initializers, as the compiler can calculate the division and initialize the variable RAD_TO_DEGREES
to the proper constant.
But if you put something like:
double SIN_OF_30 = cos(30.0 / RAD_TO_DEGREES);
let's see what happens.... the compiler interprets the expression and calculates the argument to the sin
function first. RAD_TO_DEGREES
happens to be 57.2957795
which is known at compile time (be careful, it isn't if you have defined a variable with that value and initialised with it, and you use the variable in the calculation, as the variable has only that value is something that only you know, the compiler doesn't). This leads to the argument value of 0.5235988
, but then comes the problem. The sin(3)
function is a mathematical function that comes from the mathematical standard library, so the compiler has to execute it to compute the final value of 0.5
, so this is NOT A CONSTANT EXPRESSION, even when the final variable has defined as const
. Only if you avoid to pass through sin()
you have a constant expression. So you can use
double SIN_OF_30 = 0.5;
double COS_OF_30 = 0.8660;
but not
double SIN_OF_30 = sin(30.0 / RAD_TO_DEG);
double COS_OF_30 = cos(30.0 / RAD_TO_DEG);
for automatic variables, this doesn't hold, as the initialization is done at run time, so the compiler generates the neccessary code to initialize them
int main()
{
double SIN_OF_30 = sin(30.0 / RAD_TO_DEG);
double COS_OF_30 = cos(30.0 / RAD_TO_DEG);
}
is perfectly valid. Each time you enter the main function, two variables are created in the stack, and initialised with the values result of the expressions you put there (which don't have to be constant, compile-time expressions).