Bug 114051 - Incorrect out-of-bounds warning in loop that never executes, due to failure to eliminate a dead branch
Summary: Incorrect out-of-bounds warning in loop that never executes, due to failure t...
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 13.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic, missed-optimization
Depends on:
Blocks: Wstringop-overflow
  Show dependency treegraph
 
Reported: 2024-02-22 10:19 UTC by Filip Strömbäck
Modified: 2024-02-22 14:50 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
Preprocessed version of bug.c, in case it helps. (386 bytes, text/plain)
2024-02-22 10:19 UTC, Filip Strömbäck
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Filip Strömbäck 2024-02-22 10:19:19 UTC
Created attachment 57493 [details]
Preprocessed version of bug.c, in case it helps.

When I compile the program below using: gcc -O3 bug.c

--------------
extern int puts(const char *);
#define CHAR_BIT 8

typedef unsigned long long ULongest;
#define MPS_WORD_WIDTH (sizeof(void *) * CHAR_BIT)

static void WriteULongest(ULongest w, unsigned base,
						unsigned width)
{
	static const char digit[16 + 1] = "0123456789ABCDEF";
    /* + 1 for terminator: unused, but prevents compiler warning */
	static const char pad = '0'; /* padding character */
	char buf[MPS_WORD_WIDTH + 1]; /* enough for binary, */
	/* plus one for terminator */
	unsigned i;

	/* Add digits to the buffer starting at the right-hand end, so that */
	/* the buffer forms a string representing the number.  A do...while */
	/* loop is used to ensure that at least one digit (zero) is written */
	/* when the number is zero. */
	i = MPS_WORD_WIDTH;
	buf[i] = '\0';
	do {
		--i;
		buf[i] = digit[w % base];
		w /= base;
	} while(w > 0);

	/* If the number is not as wide as the requested field, pad out the */
	/* buffer with zeros. */
	while(i > MPS_WORD_WIDTH - width) {
		--i;
		buf[i] = pad;
	}

	puts(&buf[i]);
}


int main(int argc, const char *argv[]) {
	(void)argv;
	WriteULongest((ULongest)argc, 16, 1); // Note: Occurs for width = 0 and width = 1
	return 0;
}
--------------


I get the following warning:

----------------
In function 'WriteULongest',
    inlined from 'main' at bug.c:44:2:
bug.c:35:24: warning: '__builtin_memset' writing 4294967232 bytes into a region of size 2 overflows the destination [-Wstringop-overflow=]
   35 |                 buf[i] = pad;
      |                 ~~~~~~~^~~~~
bug.c: In function 'main':
bug.c:15:14: note: at offset 63 into destination object 'buf' of size 65
   15 |         char buf[MPS_WORD_WIDTH + 1]; /* enough for binary, */
      |              ^~~
----------------

This happens when the last parameter (width) to the function WriteULongest is known to be either zero or one (see comment in the file). By inspecting the generated code (with -S), it seems like GCC transforms the second loop into a call to memset guarded by an if-statement similarly to below:

if (i > 63) {
   memset(buf, '0', 4294967232);
}

The call to memset is indeed incorrect. However, GCC fails to consider that "i > 63" is never true, since "i" is initialized to 64 and then always decreased at least once in the previous loop. The call to memset could therefore be removed entirely.



Output of gcc -v:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 13.2.0-13' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-13-13.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-13-13.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.2.0 (Debian 13.2.0-13)