Quantcast
Channel: Intel® C++ Compiler
Viewing all articles
Browse latest Browse all 1616

Code optimization problem with icl 16.0

$
0
0

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


Viewing all articles
Browse latest Browse all 1616

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>