Compilation error: expression must have a constant value

Compilation error: expression must have a constant value

mtlroom's picture

I noticed that some of my code doesn't compile anymore with latest intel compiler (C++ Compiler XE 12.0.0.104 [IA-32]).

If file is .cpp the it compiles, if it's .c then it creates a error.

Here's the code:

static const int i1 = 1;
static const int i2 = 2;
static const int a2[] = {i1, i2};

1>test.c(3): error: expression must have a constant value
1> static const int a2[] = {i1, i2};

Do I need to add some switches (I think I use qstd=99 or whatever is gcc's equivalent to enable c99 mode). Is this code not correct in C?

14 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
mtlroom's picture

Another issue that I encountered: sometimes the error message format isn't good.

C:\work\ffmpeg\libpostproc\postprocess_template.c(3546) (col. 29): catastrophic error: cannot match asm operand constraint

Because of that (col. 29) thing clicking the error message (or pressing F4) doesn't jump to source code anymore. This problem happens in VS2008, I don't have 2010.

mtlroom's picture

How can I set it to behave the way it used to in previous version?

Andreas Klaedtke's picture

gcc version 4.3.2-1.1 reports this (with or without -std=c99):
t.c:3: error: initializer element is not constant
t.c:3: error: (near initialization for 'a2[0]')
t.c:3: error: initializer element is not constant
t.c:3: error: (near initialization for 'a2[1]')

icc version 12.0.0 20101006 [IA32] reports this:
t.c(3): warning #2591: use of a const variable in a constant expression is nonstandard in C
static const int a2[] = {i1, i2};
^

t.c(3): warning #2591: use of a const variable in a constant expression is nonstandard in C
static const int a2[] = {i1, i2};

icpc version 12.0.0 20101006 works.

I hope this helps.

Andreas Klaedtke's picture

Just checked the C99 standard document (accessible through Wikipedia).
It says in section 6.7.8 (Initialization), point 4:
"All the expressions in an initializer for an object that has static storage duration shall be
constant expressions or string literals."
The definition of constant expressions seems to be a bit vague to me:
"An implementation may accept other forms of constant expressions"
I have tested several more compilers that I have access to, and they all do not allow the example you provided, so I would assume that this is not "standard c99".

mtlroom's picture

Yes, you are right. I checked that myself also. Basically, file-level constants cannot be initialized with other constant variables (in c++ it's ok). This used to work with previous version of icl.

The reason I need that is because I have MANY const static variables that are used in inline assembly. At some point I reported a bug (or weird behavior) that icl generated extramle bad code when loading these statics into mmx (or sse) registers. In short, instead of doing mmov (or whatever the instruction called in intel asm) it used to push immidiate value of the static const to the stack and then loading it from stack memory. My asm uses alot ot const static variables and the asm pushes register file to the max. As a result of that behavior the code became pile of junk. As I recall I got reply that the issue was addressed and was fixed. I tested it in the last version of 11.x compiler and the problem was still there.

So... what I did is similar to that simple test code (at the top) and then instead of loading register from i1 or i2 I load it from a2[0] or from a2[1] and in that case icl doesn't try to do the "smart optimization" and loads directly from the memory without pushing it to the stack.

The problem I have: all these 64-bit constatnts are coming form external project and then change all the time and it I don't initilize arrays with values of other constants then I'll have wrong values eventually when some of the values will be changed.

That's why, I would like to know if it's possbile to enable old behavior to initilize constatnts with other constants, or... was the problem with inline asm and loading static constants fixed so that I would need to do all that trickery for intel compiler.

mecej4's picture

I think that you have relied on compiler behavior and CPU characteristics that are subject to change and not under your control or awareness.

On many X86 processors (I have not checked the instruction sets of all of them!), the CPU and FPU have to pass data through memory rather than register-to-register. One notable exception is the FNSTSW and FLDCW, which were made capable of storing/loading from the AX/EAX register after the 8086 became obsolete. Similarly, integer registers can be loaded with immediate operands, but not FPU registers.

mtlroom's picture

Not, it has nothing to do with what you say. It was plain bug in optimizer I think (it generated slow code, but still correct). Here's the piece of code that I used for test (the set_mm0 and set_mm0_masm are identical functions in gas and masm style assembler):

static const unsigned long long C64 = 0x1234567844441111ULL;
void set_mm0()    
{    
	__asm__ volatile ("movq %0, %%mm0" ::"m"(C64));    
}    

void set_mm0_masm()    
{    
	__asm movq    mm0, C64;    
}

Both of these functions should create this asm code:

PUBLIC _set_mm0
   movq mm0, QWORD PTR [_C64]
   ret

AND finally, unbielevable, that's exactly what XE 12.0.0.104 generates!!
Here's what 11.1.082 used to generate for set_mm0:

PUBLIC _set_mm0
  sub       esp, 8
  mov       eax, 1145311505
  mov       edx, 305419896
  mov       DWORD PTR [esp], eax
  mov       DWORD PTR [4+esp], edx
  movq      mm0, QWORD PTR [esp]
  add       esp, 8
  ret

It's hard to argue, but that is pile of crap. Not only it's very slow, it also made hand tuned asm broken since there wasn't enough registers (because all that moves took extra registers), and that's the reason I found out about this problem.

So, I can now remove my static const arrays that use other static const variables in initializer lists, but that means that this code won't be compilable with icl 11.xxx. That's why i'd prefer to make 12.xxx accept const variables as initializers to other static consts. I didn't check, but that's most likely how MS broken c compiler behaves and icl ames to have the same bugs on windows ;)

mecej4's picture

OK, now that you have shown some examples of your inline ASMs, I see what you mean. On Linux-X64, for your code, gcc -O2 produces the single instruction

movq C64(%rip), %mm0

which, if such ASMs are used heavily in your code, may make your objects shorter because the IP-offsets to your constants in memory are probably smaller than full 64-bit addresses. However, this need not be the fastest option.

On the same platform, Intel C 11.1.073-X64 produces

        movq      $0x1234567844441111, %rax                     #4.0
        movq      %rax, -8(%rsp)                                #4.0
        movq      -8(%rsp), %mm0                                #4.0

which seems to indicate that the optimizer thinks that it is more efficient to turn your constants into immediate constants, which have to be first loaded into local stack memory and then into mmx registers. Since this memory is likely to be in the cache, these three instructions may run a bit faster than loading from out-of-cache .DATA segment memory, particularly if the constant is not in the cache.

But, we also see the limitations of the instruction set here. Immediate values cannot be loaded into mmx, but need to be sent through an integer register, e.g.

movq $0x123456789abcdef0,%rax
movq %rax, %mm0

or through memory. I don't know how to coax this out of the compiler without coding two corresponding #asm statements myself.

In 32-bit code, the limitations are more and more instructions may be needed. However, the example you showed in #7 is certainly not good code.

Just a couple of days ago, someone else posted a similar problem with the Fortran compiler. A "print *,x/y" statement, with x and y double complex, with optimization turned on, produced over 50 instructions.

styc's picture

I wrote the post on ifort you referred to. But I believe that issue belongs to a different aspect of compiler optimization that yours. What you have here is bad code generation, i.e., translation of low-level IL to inefficient machine code. The issue I raised the other day cannot be resolved by cleverer code generation (and an engineer who adamantly denies it). The performance is already doomed when the compiler decides to generate full complex-by-complex division for complex-by-real division.

mecej4's picture

Sure, there are differences. I paid attention to the similarity, that the programmer wants something done efficiently without running afoul of the language standard, and there are difficulties in getting the compiler to cooperate in doing so.

With regard to the last sentence in #9: the decision is not entirely up to the compiler.

The wording in section 7.1.2 of the Fortran standard is (underlining added by me)

"if the operands have dierent types or kind type parameters, the eect is as if each operand that diers in type or kind type parameter from those of the result is converted to the type and kind type parameter of the result before the operation
is performed"

On the other hand, the book by Metcalf, Reid and Cohen, Fortran 95/2003 Simplified, displays in Table 3.1 explicitly the prescription for

a.op.b

to be calculated as

a.op.cmplx(b,0,kind(a)).

I interpret the standard to mean that the compiler may (i) literally do the operations covered by the "as if", or (ii) do something more efficient but in that case be prepared to prove that the result will be "the same as if..".

The first choice clearly satisfies the standard, whereas the second choice is efficient but the burden of proving "the same as if" has to be borne.

styc's picture

Indeed, (a + bi)/c should be evaluated as (a + bi)/(c + 0i), but only conceptually. What the standard does not prescribe is what formula to use to actually arrive at (a + bi)/(c + 0i) = a/c + b/c i. You can either evaluate a/c and b/c directly (like gfortran does even at -O1), or go the incredibly twisted way of computing (ac + 0*b)(1/c^2) + (bc - 0*a)(1/c^2) i (like ifort does at -O3). The obvious ugliness in the latter case is the multiplications and additions by zero, not to mention that all arithmetic is perfomed using x87 instructions, with division computed to full 64-bit precision. Furthermore, isn't it even harder argue that ac(1/c^2) is more "the same as if" than a/c itself?

mtlroom's picture

By the way, to make it clear, I use icl, the windows version of the compiler. I'm well aware about the fact that it is better to load a single const static variable using that slowest code if that variable is used/reloaded many times in the asm block (penalty for the worst load is better than penalty for cache miss I guess). On top of that, in my real code that suffered that problem, I had like 5-6 constatnts that had to be loaded and the asm block was real big.
First of all, these (and many other) constants are used quaite alot across my functions and ****ty slow load for 5-6 of them is probaly worse than realoading it from other section (which is often used anyways). The other problem, is that the compiler fails to compile that code and generates compilation error (error is something like impossible constraint in asm statement). That happens because that load consumes extra registers and the asm block uses register file to the max, as a reuslt, compiler obviously can't compile it.

Anyways, back the original question. Is it possible to enable cl-like behavior (the way MS c-compiler behaves) with intel compiler? If it's possible, how?
thanks

Jennifer J. (Intel)'s picture

Hi,
I read through this whole thread. There're several issues discussed here.

1. the diagnostic issue: double click did not take you to the source line. this is been reported. It is a bug -DPD200163038.

2. the compile-time error: test.c(3): error: expression must have a constant value

I'm not sure if it's a bug. VS2008 also give a similar error - see below. Adding /TP option, works-around the issue. is this an ok work-around for you?

cl /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /MDd /Fo"Debug\" /Fd"Debug\vc90.pdb" /W3 /c /ZI /TC .\test.c
test.c
c:\jenn_share\issues\hello9\test.c(4) : error C2099: initializer is not a constant
c:\jenn_share\issues\hello9\test.c(4) : error C2099: initializer is not a constant

3. about the inline-asm issue, looks like it's fixed in 12.0.

Thanks,
Jennifer

Login to leave a comment.