I have come across what I think is a code optimization problem with icl version 16.0. I'm running on Windows using this:
Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64,
Version 16.0.0.110 Build 20150815
Here's my test program:
#include <stdio.h> typedef struct { int init1; int init2; } my_options; void init_options(my_options *options) { options->init1 = -11111; options->init2 = -11111; } void check_options_init(my_options *options) { if ((options->init1 != -11111) || (options->init2 != -11111)) { printf("XXXX error: options struct is not initialised\n"); printf("options->init1 = %d\n", options->init1); printf("options->init2 = %d\n", options->init2); printf("(options->init1 != -11111) = %d\n", (options->init1 != -11111)); printf("(options->init2 != -11111) = %d\n", (options->init2 != -11111)); printf("(options->init1 != -11111) || (options->init2 != -11111) = %d\n", (options->init1 != -11111) || (options->init2 != -11111)); } else printf("OK - options struct is correctly initialised\n"); } int main (void) { my_options options; init_options(&options); check_options_init(&options); return 0; }
The main program declares a variable of structure type my_options, which contains two integers. It calls a function init_options() to initialise the two integers in the variable. It then calls another function check_options_init() which is supposed to check whether the two integers have been initialised as expected.
I compile from a command line like this:
icl /Od /Z7 /Femain.exe main.c
With older versions of icl (I've tried 15.0, 14.0, 13.0, 12.0) this all works correctly, and the message
OK - options struct is correctly initialised
gets printed. With icl 16.0, though, I get this output:
XXXX error: options struct is not initialised
options->init1 = -11111
options->init2 = -11111
(options->init1 != -11111) = 0
(options->init2 != -11111) = 0
(options->init1 != -11111) || (options->init2 != -11111) = 0
Tracing through the assembly with the Visual Studio debugger it seems pretty clear what the problem is. When compiled with icl 15.0, this is the assembly code generated for function check_options_init():
void check_options_init(my_options *options) { 000000013F72104D push rbp 000000013F72104E sub rsp,50h 000000013F721052 lea rbp,[rsp+20h] 000000013F721057 mov qword ptr [rbp+20h],rbx 000000013F72105B mov qword ptr [options],rcx if ((options->init1 != -11111) || (options->init2 != -11111)) 000000013F72105F mov rax,qword ptr [options] 000000013F721063 mov eax,dword ptr [rax] 000000013F721065 cmp eax,0FFFFD499h 000000013F72106A jne check_options_init+31h (013F72107Eh) 000000013F72106C mov rax,qword ptr [options] 000000013F721070 mov eax,dword ptr [rax+4] 000000013F721073 cmp eax,0FFFFD499h 000000013F721078 je check_options_init+111h (013F72115Eh) { printf("XXXX error: options struct is not initialised\n");
Notice that the line
if ((options->init1 != -11111) || (options->init2 != -11111))
got compiled into two seprate checks on init1 and init2, using register eax, and the constant 0FFFFD499h is -11111.
With icl 16.0 though, this is the assembly:
void check_options_init(my_options *options) { 000000013F176661 push rbp 000000013F176662 sub rsp,50h 000000013F176666 lea rbp,[rsp+20h] 000000013F17666B mov qword ptr [rbp+20h],rbx 000000013F17666F mov qword ptr [options],rcx if ((options->init1 != -11111) || (options->init2 != -11111)) 000000013F176673 mov rax,qword ptr [options] 000000013F176677 mov rax,qword ptr [rax] 000000013F17667A cmp rax,0FFFFFFFFFFFFD499h 000000013F176680 je check_options_init+105h (013F176766h) { printf("XXXX error: options struct is not initialised\n");
The two separate checks have been merged into one, so just before the compare instruction at address 000000013F17667A, the register rax contains the value 0FFFFD499FFFFD499h - i.e. two consecutive copies of -11111. The problem is that the constant being compared with is given as 0FFFFFFFFFFFFD499h instead of the correct 0FFFFD499FFFFD499h.
If I modify my struct definition to add a padding item between the two integers, e.g. like this:
typedef struct { int init1; double pad; int init2; } my_options;
then the problem goes away - the 16.0 compiler generates two separate comparisons using register eax instead of combining into a single comparison with rax, and the assembly looks similar to that generated with the 15.0 and older compilers.
It looks to me like this is a code optimization gone wrong - although I'm compiling with optimization switched off. It's causing me bother because I'm building a large library with many files which get caught by this problem.
Mick Pont