Intel i++ Lite miscompilation bug

The following C code was found to produce the wrong result in Intel i++ Lite.

./bug.c

#include "HLS/hls.h"

static volatile int32_t a[9][1][7];

component int result() {
  int tmp = 1;
  for (int b = 0; b != 2; b++) {
    a[0][0][0] = 3;
    a[0][0][0] = a[0][0][0];
  }
  for (int i = 0; i < 9; i++)
    for (int k = 0; k < 7; k++)
      tmp ^= a[i][0][k];
  return tmp;
}

int main() {
  printf("%X\n", result());
}

To reproduce the bug, we need the main Makefile that Intel provides in its examples to link against the right libraries when simulating the C code.

./Makefile

SOURCE_FILES  := test_Mod.cpp
HLS_CXX_FLAGS :=
CXXFLAGS := -I/usr/include/x86_64-linux-gnu/c++/4.8
CXX := i++
override CXXFLAGS := $(CXXFLAGS) -Wno-c++11-narrowing
VERBOSE := 1

# OS-dependant tools
ifeq ($(OS),Windows_NT)
  RM  := rd /S /Q
else
  RM  := rm -rf
endif

ifeq ($(MAKECMDGOALS),)
  $(info No target specified, defaulting to test-x86-64)
  $(info Available targets: test-x86-64, test-fpga, test-gpp, clean)
endif

# Any tools installed with HLS can be found relative to the location of i++
HLS_INSTALL_DIR := $(shell which i++ | sed 's|/bin/i++||g')

# Run the i++ x86 test by default
.PHONY: default
default: test-x86-64

# Compile the component and testbench using g++ and run them as a regular program
.PHONY: test-gpp
test-gpp: CXX := clang++
test-gpp: CXXFLAGS := $(CXXFLAGS) -fsanitize=undefined -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-narrowing -Wno-unused-parameter -I"$(HLS_INSTALL_DIR)/include" -L"$(HLS_INSTALL_DIR)/host/linux64/lib" -lhls_emul -o test-gpp
test-gpp: $(SOURCE_FILES)
        $(CXX) $(SOURCE_FILES) $(CXXFLAGS)
        @echo "+-------------------------------------+"
        @echo "| Run ./test-gpp to execute the test. |"
        @echo "+-------------------------------------+"

# Run the testbench and the component as a regular program
.PHONY: test-x86-64
test-x86-64: CXXFLAGS := $(CXXFLAGS) $(HLS_CXX_FLAGS) -march=x86-64 -o test-x86-64
test-x86-64: $(SOURCE_FILES)
        $(CXX) $(SOURCE_FILES) $(CXXFLAGS)
        @echo "+----------------------------------------+"
        @echo "| Run ./test-x86-64 to execute the test. |"
        @echo "+----------------------------------------+"

# Run a simulation with the C testbench and verilog component
.PHONY: test-fpga
ifeq ($(VERBOSE),1)
  test-fpga: CXXFLAGS := $(CXXFLAGS) -v
endif
test-fpga: CXXFLAGS := $(CXXFLAGS) $(HLS_CXX_FLAGS) -march=Arria10 -o test-fpga
test-fpga: $(SOURCE_FILES)
        $(CXX) $(SOURCE_FILES) $(CXXFLAGS)
        @echo "+--------------------------------------+"
        @echo "| Run ./test-fpga to execute the test. |"
        @echo "+--------------------------------------+"

# Clean up temprary and delivered files
CLEAN_FILES := test-gpp \
               test-gpp.prj \
               test-fpga \
               test-fpga.prj \
               test-x86-64 \
               test-x86-64.prj
clean:
        -$(RM) $(CLEAN_FILES)

Then the bug can be reproduced by running make, which also runs vsim to simulate the produced Verilog. First, we can generate the golden output for the C file.

make test-gpp >make.log 2>&1
./test-gpp >test-gpp.log 2>&1
cat test-gpp.log
2

Then we can generate the actual output that Intel produces.

make test-fpga >make.log 2>&1
./test-fpga >test-fpga.log 2>&1
cat test-fpga.log
0