-fshort-wchar causes incorrect code generation with -O2/-Os
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Using `-fshort-wchar` with clang++ produces incorrect results that vary depending on the optimization level.
I wrote a `char16_t` string length function. However, it does not return the correct value when compiled with both `-fshort-wchar` and `-O2` flags.
compiler: clang++-22
```
Debian clang version 22.1.2 (1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-22/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/12
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/13
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/14
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/15
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/15
Candidate multilib: .;@m64
Selected multilib: .;@m64
```
I've tested with the following code
**u16len.cpp**:
```cpp
#include
#include
size_t u16len(const char16_t* s)
{
const char16_t* e = s;
while (*e) {
++e;
}
return e - s;
}
size_t uint16len(const uint16_t* s)
{
const uint16_t* e = s;
while (*e) {
++e;
}
return e - s;
}
```
**u16.cpp**:
```cpp
#include
#include
extern size_t u16len(const char16_t* s);
extern size_t uint16len(const uint16_t* s);
int
main()
{
printf("wchar_t size: %zu\n", sizeof(wchar_t));
char16_t str[] = u"0123456789";
size_t l1 = sizeof(str) / sizeof(str[0]) - 1;
size_t l2 = u16len(str);
printf("len: u16len: %zu == %zu\n", l1, l2);
size_t l3 = uint16len((const uint16_t*)str);
printf("len: uint16len: %zu == %zu\n", l1, l3);
return 0;
}
```
**Makefile**:
```make
CXX = clang++-22
CXXFLAGS = -fshort-wchar
OPTFLAGS =
run: run0 run2 runs
run0: OPTFLAGS = -O0
run0:
$(CXX) $(CXXFLAGS) -c u16len.cpp $(OPTFLAGS)
$(CXX) $(CXXFLAGS) -c u16.cpp $(OPTFLAGS)
$(CXX) $(CXXFLAGS) u16.o u16len.o
./a.out
run2: OPTFLAGS = -O2
run2:
$(CXX) $(CXXFLAGS) -c u16len.cpp $(OPTFLAGS)
$(CXX) $(CXXFLAGS) -c u16.cpp $(OPTFLAGS)
$(CXX) $(CXXFLAGS) u16.o u16len.o
./a.out
runs: OPTFLAGS = -Os
runs:
$(CXX) $(CXXFLAGS) -c u16len.cpp $(OPTFLAGS)
$(CXX) $(CXXFLAGS) -c u16.cpp $(OPTFLAGS)
$(CXX) $(CXXFLAGS) u16.o u16len.o
./a.out
```
Result:
```
clang++-22 -fshort-wchar -c u16len.cpp -O0
clang++-22 -fshort-wchar -c u16.cpp -O0
clang++-22 -fshort-wchar u16.o u16len.o
./a.out
wchar_t size: 2
len: u16len: 10 == 10
len: uint16len: 10 == 10
clang++-22 -fshort-wchar -c u16len.cpp -O2
clang++-22 -fshort-wchar -c u16.cpp -O2
clang++-22 -fshort-wchar u16.o u16len.o
./a.out
wchar_t size: 2
len: u16len: 10 == 5
len: uint16len: 10 == 5
clang++-22 -fshort-wchar -c u16len.cpp -Os
clang++-22 -fshort-wchar -c u16.cpp -Os
clang++-22 -fshort-wchar u16.o u16len.o
./a.out
wchar_t size: 2
len: u16len: 10 == 5
len: uint16len: 10 == 5
```
The compiler seems to be calling **wcslen** instead of the original code when -O2 is enabled.
Contributor guide
Assessment
This issue has not been assessed yet.