Comments (16)
If you click the orange checkmark next to the compiler flags you can see the compiler flags added by CE. Additionally CE does a lot of filtering of assembly to provide something more human-readable. If you turn off filters you can see something more accurate, and if you turn on link to binary it will show the objdump’d code from the final executable.
from compiler-explorer.
The printf->puts optimization isn’t done by CE, that’s gcc
from compiler-explorer.
The printf->puts optimization isn’t done by CE, that’s gcc
Of course. My point was to compare CE against my computer. And that however CE is running gcc, I find it surprising that I can't run the same version of gcc on my computer and get the same or substantially similar output.
from compiler-explorer.
Your function returning a string view is not returning anything, this is UB. Turn the function void and it works (note your main is also not returning anything, should be void also).
https://gcc.godbolt.org/z/Mo1dqzcr6
Probably caused by some optimizers trying to reuse the returned value as main is missing a ret after the call in your faulty case... Definitely not a CE bug.
from compiler-explorer.
Respectfully, I think you are missing my point and I don't think this issue should be closed. I'm well aware that it's undefined behavior: I said so in the first sentence of my post. Fixing my code is completely beside the point. I know what's wrong with it.
Some of us use Compiler Explorer to "Explore" the behavior of the "Compiler" in undefined behavior scenarios.
The issue I am trying to draw your attention to is that CE is showing assembly code that it could not possibly be executing -- regardless of how many filter buttons one clicks or unclicks. This diminishes the educational and investigative value of Compiler Explorer -- in this case.
(note your main is also not returning anything, should be void also).
This is not correct. The standard makes it clear that main must be defined to return int, and the standard also allows the programmer to omit the return statement. See 6.9.3.1 subparts (2.1) and (3.4) from e.g. here https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4928.pdf "If control flows off the end of the compound-statement of main, the effect is equivalent to a return with operand 0 (see also 14.4)"
Regardless, people like myself use Compiler Explorer to analyze even buggy programs. Trying to spot bugs in the source is completely beside the point. My issue here is that the assembly doesn't match the execution, and I continue to maintain that that's surprising CE behavior from a UX standpoint.
from compiler-explorer.
The assembly code IS showing what it is executing
In the assembly you can see there is no ret
opcode at the end of main
, which will cause issues depending on what's after main, because the CPU will just continue on executing whatever is there
If you select Link to binary
, there's some more clues as to the ordering of the actual binary, but we don't show more than the labels that are involved in the actual user code, because it would be a bad user experience.
If you want to see everything, your best bet is to compile locally and debug the program to see what's after it.
With objdump alone you might not figure it out, because objdump will probably think it's data instead of parsing it as opcodes, but it also depends on how the program is copied into memory.
from compiler-explorer.
Hi,
I can clarify better now that I'm at a computer. The flags CE is passing are -g -o /nosym/tmp/compiler-explorer-compiler2024415-7782-uf1da5.2mpt/output.s -masm=intel -S -fdiagnostics-color=always -O2 -std=c++17 /nosym/tmp/compiler-explorer-compiler2024415-7782-uf1da5.2mpt/example.cpp
. Nothing here would affect the assembly, substance-wise. It's using the same flags for execution minus the -masm=intel -S
. Theoretically the fact we compile twice could be problematic for a non-deterministic compiler, but nothing here should be non-deterministic.
If you look at the link to binary objdump output https://gcc.godbolt.org/z/PMsEv5abe you'll see the linker puts main
before func
as opposed to gcc's output which puts func
first. This isn't any inconsistency on CE's behalf, it's just whatever choices gcc/the linker make.
As far as differences between CE and your local gcc, it could be a simple version difference or a difference in how we compile gcc (more info at https://github.com/compiler-explorer/gcc-builder), or it could also be a difference in your local linker.
I'm not sure how CE's gcc could do the printf
-> puts
optimization while your local gcc doesn't, unless you're using a really old gcc. If you could provide more info on how you're testing this locally I'd be interested to look into that more.
I agree 100% CE is a special tool that we turn to for debugging things, but unfortunately I don't think this is a CE bug. There are just so many factors at play, it'd be impossible for CE to surely show you the same thing you see locally.
from compiler-explorer.
Thanks very much for your reply. As for my gcc I was careful to use the same version I picked for CE (11.4.0). Verbose output below, which also shows the same flags, source code, but different execution behavior. I tried to use exactly the same flags but I changed the pathnames and I didn't use -S and -o (because I wanted an executable).
That said, I'm not overly concerned that CE has different behavior than my own compiler (as you said, there could be a difference in compiler build -- though I admit the printf -> puts optimization continues to surprise me. My compiler isn't doing that with those flags). I'm more concerned that on my box, the assembly feels consistent with the execution (print message once, then segfault). Put another way, on my box, I can explain how the assembly leads to the execution.
However on CE I can't explain how the assembly is consistent with the execution. Namely, regardless of where func
and main
are laid out by the linker, where is the branch instruction that causes one to jump back to the other, or back to itself? Are we to assume that some random piece of data that appears at the end of main just happens to be interpreted as a branch to exactly the right target?
Again, that's the gist of my complaint here... that the assembly CE is showing feels like it can't be the assembly that CE is running, unless one makes some very special assumptions.
I'm using the gcc 11.4.0 that comes out of the box with Ubuntu 22.04.
$ cat example.cpp && g++ -v && g++ -g -masm=intel -fdiagnostics-color=always -O2 -std=c++17 example.cpp && ./a.out
#include <stdio.h>
#include <string_view>
std::string_view func(const int &) {
printf("hello\n");
}
int main() {
int m = 5;
func(m);
}
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/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='Ubuntu 11.4.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --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/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
example.cpp: In function ‘std::string_view func(const int&)’:
example.cpp:6:1: warning: no return statement in function returning non-void [-Wreturn-type]
6 | }
| ^
hello
Segmentation fault (core dumped)
from compiler-explorer.
Thanks, I’ll look more this evening. I’m also surprised by the infinite loop, I can only guess it must be something after func that CE is filtering out of the objdump.
from compiler-explorer.
I did some digging. But it doesn't look like we can reproduce the behavior of Ubuntu GCC's version.
The things that we can reproduce is a bunch of extra opcodes and disabling the printf-puts optimization:
-fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection -fno-builtin-printf -fPIE -pie
What we cannot reproduce is because of Ubuntu using __printf_chk
instead of printf
(probably), this subsequently causes that func()
is not inlined and very different assembly is generated.
A comparison:
With CE's gcc (with the extra parameters):
0000000000001060 <main>:
1060: f3 0f 1e fa endbr64
1064: 50 push %rax
1065: 58 pop %rax
1066: 48 8d 3d 97 0f 00 00 lea 0xf97(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
106d: 31 c0 xor %eax,%eax
106f: 48 83 ec 08 sub $0x8,%rsp
1073: e8 d8 ff ff ff call 1050 <printf@plt>
1078: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
107f: 00
0000000000001080 <_start>:
1080: f3 0f 1e fa endbr64
1084: 31 ed xor %ebp,%ebp
1086: 49 89 d1 mov %rdx,%r9
1089: 5e pop %rsi
108a: 48 89 e2 mov %rsp,%rdx
108d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1091: 50 push %rax
1092: 54 push %rsp
1093: 45 31 c0 xor %r8d,%r8d
1096: 31 c9 xor %ecx,%ecx
1098: 48 8d 3d c1 ff ff ff lea -0x3f(%rip),%rdi # 1060 <main>
109f: ff 15 23 2f 00 00 call *0x2f23(%rip) # 3fc8 <__libc_start_main@GLIBC_2.34>
10a5: f4 hlt
10a6: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
10ad: 00 00 00
With Ubuntu's GCC:
0000000000001060 <main>:
1060: f3 0f 1e fa endbr64
1064: 50 push %rax
1065: 58 pop %rax
1066: 48 83 ec 18 sub $0x18,%rsp
106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1071: 00 00
1073: 48 89 44 24 08 mov %rax,0x8(%rsp)
1078: 31 c0 xor %eax,%eax
107a: 48 8d 7c 24 04 lea 0x4(%rsp),%rdi
107f: e8 fc 00 00 00 call 1180 <_Z4funcRKi>
1084: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
108b: 00 00 00
108e: 66 90 xchg %ax,%ax
0000000000001090 <_start>:
1090: f3 0f 1e fa endbr64
1094: 31 ed xor %ebp,%ebp
1096: 49 89 d1 mov %rdx,%r9
1099: 5e pop %rsi
109a: 48 89 e2 mov %rsp,%rdx
109d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
10a1: 50 push %rax
10a2: 54 push %rsp
10a3: 45 31 c0 xor %r8d,%r8d
10a6: 31 c9 xor %ecx,%ecx
10a8: 48 8d 3d b1 ff ff ff lea -0x4f(%rip),%rdi # 1060 <main>
10af: ff 15 23 2f 00 00 call *0x2f23(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>
10b5: f4 hlt
10b6: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
10bd: 00 00 00
Somewhat further in the binary:
0000000000001180 <_Z4funcRKi>:
1180: f3 0f 1e fa endbr64
1184: 50 push %rax
1185: 58 pop %rax
1186: 48 8d 35 77 0e 00 00 lea 0xe77(%rip),%rsi # 2004 <_IO_stdin_used+0x4>
118d: bf 01 00 00 00 mov $0x1,%edi
1192: 31 c0 xor %eax,%eax
1194: 48 83 ec 08 sub $0x8,%rsp
1198: e8 b3 fe ff ff call 1050 <__printf_chk@plt>
00000000000011a0 <_fini>:
11a0: f3 0f 1e fa endbr64
11a4: 48 83 ec 08 sub $0x8,%rsp
11a8: 48 83 c4 08 add $0x8,%rsp
11ac: c3 ret
from compiler-explorer.
You can reproduce by not using printf but puts directly:
#include <stdio.h>
#include <string_view>
std::string_view func(const int &) {
puts("hellkkko\n");
}
int main() {
int m = 5;
func(m);
}
➜ ~ g++ --version
g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
➜ ~ g++ -O2 -std=c++17 /tmp/toto.cc -o /tmp/toto -g3
➜ ~ /tmp/toto|head -n5
hellkkko
hellkkko
hellkkko
Using gdb shows that the return from puts jumps at the beg of main
, hence the loop. I'm not expert in x86-64 and how it handles stack/return address, but it's probably caused by the function not returning as it should.
7 │ 0000000000001050 <puts@plt>:
38 │ 1050: f3 0f 1e fa endbr64
39 │ 1054: f2 ff 25 75 2f 00 00 bnd jmp *0x2f75(%rip) # 3fd0 <puts@GLIBC_2.2.5>
40 │ 105b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
41 │
42 │ Disassembly of section .text:
43 │
44 │ 0000000000001060 <main>:
45 │ 1060: f3 0f 1e fa endbr64
46 │ 1064: 50 push %rax
47 │ 1065: 58 pop %rax
48 │ 1066: 48 8d 3d 97 0f 00 00 lea 0xf97(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
49 │ 106d: 48 83 ec 08 sub $0x8,%rsp
50 │ 1071: e8 da ff ff ff call 1050 <puts@plt>
51 │ 1076: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
Sorry if my initial answer was too short, not my intent (and you're right about void main
, my bad).
from compiler-explorer.
Using gdb shows that the return from puts jumps at the beg of
main
, hence the loop. I'm not expert in x86-64 and how it handles stack/return address, but it's probably caused by the function not returning as it should.
_start
(the "real" entry point) is right after main
, and _start
calls main
(via glibc)
from compiler-explorer.
True, but that does not explain why gcc thinks it's ok to inline func
(and ends up not having any ret
). See https://gcc.godbolt.org/z/r6jv91b8j where it even discard the return 0
in main. 🤷
from compiler-explorer.
True, but that does not explain why gcc thinks it's ok to inline
func
(and ends up not having anyret
). See https://gcc.godbolt.org/z/r6jv91b8j where it even discard thereturn 0
in main. 🤷
tbh: garbage in => garbage out (https://en.cppreference.com/w/cpp/language/return - Flowing off the end of a value-returning function, except main and specific [coroutines](https://en.cppreference.com/w/cpp/language/coroutines)(since C++20), without a return statement is undefined behavior.
)
from compiler-explorer.
Aha, interesting! Yeah that particular garbage in => garbage out scenario doesn't surprise me unfortunately. While I'm not a fan of how aggressively modern compilers exploit "undefined behavior" it does unfortunately mean that if func() has undefined behavior (even "harmless" UB like this program), and main() calls func(), then main gets to have undefined behavior too, meaning it can be arbitrarily messed-up.
from compiler-explorer.
Yes... But I'm still curious to understand why GCC ends up with this code. I may dig the dumps and ask around (but haven't got any new answer yet...).
from compiler-explorer.
Related Issues (20)
- ppc64 and ppc64le nightly cross gcc failures HOT 1
- [REQUEST]: Permit "Add Executor" to Conformance View
- [BUG]: Xtensa ESP32 LEND loop targets are filtered as directives HOT 4
- [COMPILER REQUEST]: GraalVM Native Image
- [BUG]: code compiles to either infinite loop or nothing with -O3 HOT 3
- [COMPILER REQUEST]: QuickJS
- TypeError: opcodes is not iterable
- Can't link with gcc 4.7.1 and 4.7.2 HOT 1
- [BUG]: recompiling resets scroll position to the top
- [LIB REQUEST] pycryptodome & cryptography
- [COMPILER REQUEST]: sh-elf-gdc HOT 4
- [BUG]: Mobile mode can't be edited? HOT 1
- [BUG]: Browser tab refresh resets all renamed CE tabs
- [COMPILER REQUEST]: Groovy HOT 1
- [BUG]: char8_t not declared even with -std=c23 HOT 1
- Make lines linkable HOT 3
- [BUG]: preprocessor output is empty when using MS compiler
- [BUG]: permission denied opening /dev/null HOT 5
- [BUG]: Mfem library not loading: single vs double precision not specified
- [BUG]: Internal Compiler Explorer error: Error: Unexpected basic block terminator: HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from compiler-explorer.