I thought I knew C++ *sob* It has been inserting extra code on me this whole time.
-
I'm using C++17 gcc 12.2 or whatever. relatively recent i think. i've tried it on several platforms. clearly you're better at poring over that stuff than I am. It reads like japanese stereo instructions to me, and I get lost pretty easily. I didn't realize taking the return value of a function to a function that takes variadic arguments is undefined behavior. That's very weird to me, as I would have thought it would simply evaluate the function and then stick the return value in the register. Particularly since this is a constexpr function that's const all the way through I figured it would be optimized out. And it is kind of, in that it never calls anything. Anyway, thanks. I'll leave it there, as you seem impatient. Sorry to bother you.
To err is human. Fortune favors the monsters.
Sorry, I was [wrong, about N2975](https://www.open-std.org/jtc1/sc22/WG14/www/docs/n3044.txt). It passed back in July with 17 Yes, 0 No and two abstains. I updated my post.
-
Tried on clang x86, gcc x86, gcc xtensa, gcc AVR.
To err is human. Fortune favors the monsters.
What release of gcc/clang are you using? According to [Compiler Explorer](https://godbolt.org/) I get the following with clang 5.0 with -O1 -std=C++17:
main: # @main
push rax
mov edi, .L.str
mov esi, 65
xor eax, eax
call printf
mov edi, .L.str
mov esi, 65
xor eax, eax
call printf
xor eax, eax
pop rcx
ret
.L.str:
.asciz "%c\n"And x86-64 gcc 5.1 with the same flags gives:
.LC0:
.string "%c\n"
main:
sub rsp, 8
mov esi, 65
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov esi, 65
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
add rsp, 8
retThose are both pretty old compilers - the first of their lines to support C++17 AFAICT. Both produce the same code for each call. So maybe something in the compiler flags you're passing?
Keep Calm and Carry On
-
What release of gcc/clang are you using? According to [Compiler Explorer](https://godbolt.org/) I get the following with clang 5.0 with -O1 -std=C++17:
main: # @main
push rax
mov edi, .L.str
mov esi, 65
xor eax, eax
call printf
mov edi, .L.str
mov esi, 65
xor eax, eax
call printf
xor eax, eax
pop rcx
ret
.L.str:
.asciz "%c\n"And x86-64 gcc 5.1 with the same flags gives:
.LC0:
.string "%c\n"
main:
sub rsp, 8
mov esi, 65
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov esi, 65
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
add rsp, 8
retThose are both pretty old compilers - the first of their lines to support C++17 AFAICT. Both produce the same code for each call. So maybe something in the compiler flags you're passing?
Keep Calm and Carry On
It probably has to do with the fact that I can't convince godbolt.org to allow me to remove their default compiler options and replace them with my own. I'm stuck with -o -whole-program or whatever. I used to be able to change it there somehow. Maybe someone exploited it and they turned off the feature.
To err is human. Fortune favors the monsters.
-
I'm not on a PC tonight, it takes longer to type on my TV onscreen keyboard. It's not easy! :sigh: Anyways, I found some better material for you to read. [Variadic arguments - cppreference.com](https://en.cppreference.com/w/cpp/language/variadic\_arguments#Default\_conversions)
Well it's tomorrow. In case you're curious, I took the variadic arguments out of the code. I replaced printf with putchar. Same result.
push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR \[rbp-4\], edi mov QWORD PTR \[rbp-16\], rsi mov eax, 65 ; \*\*\* movsx eax, al ; \*\*\* mov edi, eax ;\*\*\* call putchar mov edi, 65 ;\*\*\* call putchar mov eax, 0 leave ret
To err is human. Fortune favors the monsters.
-
It probably has to do with the fact that I can't convince godbolt.org to allow me to remove their default compiler options and replace them with my own. I'm stuck with -o -whole-program or whatever. I used to be able to change it there somehow. Maybe someone exploited it and they turned off the feature.
To err is human. Fortune favors the monsters.
-
I get the same results using g++ 5.5.0 on my local linux box. That would be a CentOS 7 system on which I compiled g++-5.5.0 from source. So, still wondering if its maybe the flags you're using.
Keep Calm and Carry On
Looking at your output more carefully, your initial output is similar to mine. Your final output is less optimized, probably having to do with your compiler flags.
To err is human. Fortune favors the monsters.
-
Well it's tomorrow. In case you're curious, I took the variadic arguments out of the code. I replaced printf with putchar. Same result.
push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR \[rbp-4\], edi mov QWORD PTR \[rbp-16\], rsi mov eax, 65 ; \*\*\* movsx eax, al ; \*\*\* mov edi, eax ;\*\*\* call putchar mov edi, 65 ;\*\*\* call putchar mov eax, 0 leave ret
To err is human. Fortune favors the monsters.
-
Ok, I tested this on my dev box, everything we talked about above in the C standard applies. And I get the same exact assembler output you get. Only with optimizations disabled. So I guess you have optimization disabled?
I have -o on godbolt.org and as I said somewhere else on this thread (I don't remember where or to whom) it seems to not be letting me change that. It used to, so I either can't find it again, or they've removed the feature.
To err is human. Fortune favors the monsters.
-
Ok, I tested this on my dev box, everything we talked about above in the C standard applies. And I get the same exact assembler output you get. Only with optimizations disabled. So I guess you have optimization disabled?
I have -o on. I can't seem to find how to change that at godbolt.org. I just remembered there's a GCC pragma where I can change it but I can't remember what it is, and so I'm googling now to figure out what it is. Edit: Now I feel like an idiot. I thought -o did at least minimal optimizations but maybe the switch means something different unsuffixed.
#pragma GCC optimize("Os")
That reflects the default of my IoT build environment It fixes it, so maybe I'm worrying over nothing. I wish I could actually check my production code, but it relies on the Arduino framework, and I can't run that at godbolt. I've tried disassembler extensions in VSCode but none work with platformIO because it makes its own CMake/ninja scripts for everything on the fly.To err is human. Fortune favors the monsters.
-
I have -o on. I can't seem to find how to change that at godbolt.org. I just remembered there's a GCC pragma where I can change it but I can't remember what it is, and so I'm googling now to figure out what it is. Edit: Now I feel like an idiot. I thought -o did at least minimal optimizations but maybe the switch means something different unsuffixed.
#pragma GCC optimize("Os")
That reflects the default of my IoT build environment It fixes it, so maybe I'm worrying over nothing. I wish I could actually check my production code, but it relies on the Arduino framework, and I can't run that at godbolt. I've tried disassembler extensions in VSCode but none work with platformIO because it makes its own CMake/ninja scripts for everything on the fly.To err is human. Fortune favors the monsters.
honey the codewitch wrote:
#pragma GCC optimize("Os") It fixes it
Awesome, I'm glad it's sorted out! Congratulations. Don't rely on the optimization pass. The C++ standards are correct. It's just that the optimization pass can rearrange code, remove functions and/or use intrinsics instead. The unoptimized code would be more standards compliant. :-D
-
honey the codewitch wrote:
#pragma GCC optimize("Os") It fixes it
Awesome, I'm glad it's sorted out! Congratulations. Don't rely on the optimization pass. The C++ standards are correct. It's just that the optimization pass can rearrange code, remove functions and/or use intrinsics instead. The unoptimized code would be more standards compliant. :-D
In general you're right, but in this case, there are special considerations. For starters, the toolchain is fixed to GCC, and other compilers simply don't have the backends to target what I target. So I have the luxury of using GCC specific things, and expecting GCC specific behavior, but I'm also saddled with GNU C++ vs STD C++ because the frameworks my code runs under require it, despite my code being (more) standard than GNU. That being said, I am counting on those optimizations because this is IoT, and this is critical code paths. That's why I'm looking at the asm output in the first place. :)
To err is human. Fortune favors the monsters.
-
I have -o on. I can't seem to find how to change that at godbolt.org. I just remembered there's a GCC pragma where I can change it but I can't remember what it is, and so I'm googling now to figure out what it is. Edit: Now I feel like an idiot. I thought -o did at least minimal optimizations but maybe the switch means something different unsuffixed.
#pragma GCC optimize("Os")
That reflects the default of my IoT build environment It fixes it, so maybe I'm worrying over nothing. I wish I could actually check my production code, but it relies on the Arduino framework, and I can't run that at godbolt. I've tried disassembler extensions in VSCode but none work with platformIO because it makes its own CMake/ninja scripts for everything on the fly.To err is human. Fortune favors the monsters.
honey the codewitch wrote:
I wish I could actually check my production code, but it relies on the Arduino framework, and I can't run that at godbolt. I've tried disassembler extensions in VSCode but none work with platformIO because it makes its own CMake/ninja scripts for everything on the fly.
If you are comfortable looking at assembler then you could analyze your Arduino code with [Ghidra](https://github.com/NationalSecurityAgency/ghidra).
-
honey the codewitch wrote:
I wish I could actually check my production code, but it relies on the Arduino framework, and I can't run that at godbolt. I've tried disassembler extensions in VSCode but none work with platformIO because it makes its own CMake/ninja scripts for everything on the fly.
If you are comfortable looking at assembler then you could analyze your Arduino code with [Ghidra](https://github.com/NationalSecurityAgency/ghidra).
Ooooh, you just made my morning. I was just looking for something like that and gave up at the time. Thanks. Edit: NVM it wasn't what I was thinking. I might be able to use it on my firmware.bin but I'm not sure how I would match the symbols back up to the source without it being aware of my build environment so it could load the symbols for each library's C or C++ source translation unit.
To err is human. Fortune favors the monsters.
-
honey the codewitch wrote:
Yeah, that's not really the issue I'm having though.
:laugh: That's why the code there is being generated. It's promoting the char to 32 bits. The language spec calls it "default argument promotion" I have nothing more to add. Good luck
-
So the char must be sign-extended. But that does not, and cannot (due to the as-if rule), mean that the compiler must make that happen at run time, it can trivially be done at compile time after all.
Hmmm, I'm not really sure what you're saying here. You are obviously referring to the code optimization pass. But this sentence doesn't make sense.
harold aptroot wrote:
But that does not, and cannot, mean that the compiler must make that happen at run time
Nearly every compiler will perform the sign-extending at run time with optimization disabled, I just tested 4 MSVC versions few hours ago with the code at the top of this thread. Sure, it can be trivially optimized away.
-
Hmmm, I'm not really sure what you're saying here. You are obviously referring to the code optimization pass. But this sentence doesn't make sense.
harold aptroot wrote:
But that does not, and cannot, mean that the compiler must make that happen at run time
Nearly every compiler will perform the sign-extending at run time with optimization disabled, I just tested 4 MSVC versions few hours ago with the code at the top of this thread. Sure, it can be trivially optimized away.
-
harold aptroot wrote:
I decided against any further elaboration
Because there isn't anything to elaborate. :laugh: :laugh: It's OK, we all make mistakes. I was waiting to see what you had to say though.
-
Tried on clang x86, gcc x86, gcc xtensa, gcc AVR.
To err is human. Fortune favors the monsters.
I get
main:
.LFB31:
.cfi_startproc
endbr64
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $65, %edx
leaq .LC0(%rip), %rsi
movl $1, %edi
movl $0, %eax
call __printf_chk@PLT
movl $65, %edx
leaq .LC0(%rip), %rsi
movl $1, %edi
movl $0, %eax
call __printf_chk@PLT
movl $0, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
retwith
g++ -std=c++17 -O1
(g++ 9.4
on local linux box)."In testa che avete, Signor di Ceprano?" -- Rigoletto