Live data from Hacker News

There is no 'printf'

netmeister.org

81–90 of 158 posts

Re: There is no 'printf'

#81
post #79

Compiler optimization is really annoying sometimes. It's allowed to assume that functions with the same name as standard library functions behave according to standard. They'll swap out printf calls for puts if they don't have a format. The compiler just knows it can do that. Sure, it optimizes things but it gets to the point the code no longer reflects what's written on the source file. Try to hook into printf and i…

Why is that a problem? (That's a serious question. I'm not suggesting that it isn't a problem. Apparently it is for you.) If I write printf("Hello, world\n"), all I care about is that those characters are written to the standard output stream when I run the program -- and that's all the language standard specifies. I rarely even look at the assembly or machine code. As someone else mentioned, "gcc -fno-builtin" inhib…

Because it's surprising and breaks our expectations. The author of this article clearly expected a call to printf to be present in the generated code.

> all I care about is that those characters are written to the standard output stream when I run the program

Sometimes people care about a lot more. Such as the ability to hook into a specific function or ensuring the compiler doesn't generate calls to certain functions.

> that's all the language standard specifies

The author clearly cared about the undefined behavior. He had a mental model of what would happen that was perfectly reasonable. It was constantly invalidated by the optimizer.

> I rarely even look at the assembly or machine code.

I do. It's very jarring when you write some code and the compiler deletes some of your calls and reorders the rest. It can really complicate debugging sessions.

> gcc -fno-builtin

Yeah, that's become a standard flag for me. Just checked the documentation and it turns out it's also implied by -ffreestanding which is a better language than hosted C anyway just because it gets rid of all the libc cruft.

Re: There is no 'printf'

#82
post #64

Earlier quoted context omitted.

This sounds like a compiler optimizing bug, in that if the expected behavior of main without a return is that it will return the result of the last function call, then the last function in the main does use its return value and that should not be optimized.

Yeah honestly this blog post just seems to be highlighting a bug in gcc when it is compiling to an older version of C. Not sure why the author chose to frame it as some big point about the printf function.

I think that's because the only way to see puts() return value after calling printf is in this weird situation. If you actually assign the return value somewhere and read it, the compiler can't replace it with puts and you'll get the return value you expect. But in this case the compiler is tricked into exposing that it's really calling puts().

Re: There is no 'printf'

#83
post #64

Quick Summary: The C compiler optimizer replaces printf("Hello World!\n") with puts("Hello World!\n") and the implicit return from main() changes from 13 (the return value of printf) to 10 (the return value of puts)

This sounds like a compiler optimizing bug, in that if the expected behavior of main without a return is that it will return the result of the last function call, then the last function in the main does use its return value and that should not be optimized.

As far as I understand, there is no “expected behavior of main without a return” under ANSI; it’s undefined behavior as (according to this version of the C standard) a function with an int return type must return an int. The fact that it happens to return the result of last function call is more-or-less a coincidence because the compiler doesn’t bother to clear out or reuse the return value register.

If this seems unintuitive, consider a more general example:

int foo(int x) { if (x >= 0) return 123; if (x What’s the “expected” return value if neither of the if-statements match? That seems like a nonsensical question because the conditions are clearly mutually exclusive, but if you’re a dumb enough compiler (or the conditions are more complex) you might not be able to prove that. So should it return zero? Should the compiler insert code to save and remember the result of the last function call? According to the C standard, the compiler doesn’t have to handle this situation; it is free to assume that the branches are indeed mutually exclusive, and it will most likely do whatever produces the easiest/fastest code in that case.

In the OP’s example, it seemingly just decides to return void (leaving whatever garbage was previously in the return value register for the caller to discover). But it could also have assumed that main must infinitely loop (since there’s no return statement), and omitted the return instruction altogether causing execution to “fall off” the end of the function into whatever happened to be next in memory.

Re: There is no 'printf'

#84

There is 'printf'. It's just that printf (and the rest of the standard library) is technically as much a part of the C language as the language grammar itself, and C compilers are welcome to use innate knowledge of those functions for optimizations. The other place you typically see this is calls to functions like memcpy/memset being elided to inline vector ops or CISC copies, or on simpler systems, large manual zero…

> C compilers will typically have an escape hatch for envs like deeply embedded systems and kernels like gcc's -ffreestanding and -fno-builtin that says "but for real though, don't assume std lib functions exist or you know what they are based on the function's name".

GCC requires standard library functions even on freestanding environments! It will still emit calls to those functions in certain circumstances.

https://gcc.gnu.org/onlinedocs/gcc/Standards.html

> GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.

> if __builtin_trap is used, and the target does not implement the trap pattern, then GCC emits a call to abort.

Re: There is no 'printf'

#85

Compiler optimization is really annoying sometimes. It's allowed to assume that functions with the same name as standard library functions behave according to standard. They'll swap out printf calls for puts if they don't have a format. The compiler just knows it can do that. Sure, it optimizes things but it gets to the point the code no longer reflects what's written on the source file. Try to hook into printf and i…

Do you also expect `sqrt(a)` to be `call sqrt` and not `sqrtss xmm0`?

Re: There is no 'printf'

#86

Compiler optimization is really annoying sometimes. It's allowed to assume that functions with the same name as standard library functions behave according to standard. They'll swap out printf calls for puts if they don't have a format. The compiler just knows it can do that. Sure, it optimizes things but it gets to the point the code no longer reflects what's written on the source file. Try to hook into printf and i…

Do you also expect `sqrt(a)` to be `call sqrt` and not `sqrtss xmm0`?

No. I expect that from I/O functions.

Re: There is no 'printf'

#88

Earlier quoted context omitted.

No. I expect that from I/O functions.

How is it different? Why do you draw the line at I/O function?

Because it doesn't really matter how the program calculates mathematical functions as long as the result is correct. Whether it's a function or an instruction, it's inconsequential.

I/O on the other hand is a lot more interesting. They're a very common source of undefined behavior, bugs and interception via custom shared objects. I'd rather the compiler touched them as little as possible. Actually I'd rather not use them at all. I find system calls to be much more ergonomic.

Re: There is no 'printf'

#89
post #64

Earlier quoted context omitted.

This sounds like a compiler optimizing bug, in that if the expected behavior of main without a return is that it will return the result of the last function call, then the last function in the main does use its return value and that should not be optimized.

Yeah honestly this blog post just seems to be highlighting a bug in gcc when it is compiling to an older version of C. Not sure why the author chose to frame it as some big point about the printf function.

Not a bug in GCC. The program GCC tries to compile is invalid, and the C standard says GCC can do anything it please in that case. So GCC is fine, just fix your program.

Re: There is no 'printf'

#90

Earlier quoted context omitted.

How is it different? Why do you draw the line at I/O function?

Because it doesn't really matter how the program calculates mathematical functions as long as the result is correct. Whether it's a function or an instruction, it's inconsequential. I/O on the other hand is a lot more interesting. They're a very common source of undefined behavior, bugs and interception via custom shared objects. I'd rather the compiler touched them as little as possible. Actually I'd rather not use…

What about datetime, random, system, etc?
Post reply on HN