1

Working under Linux, i just met the following issue. (For sure, someone will give me the answer, but up to now,i didn't find any simple and clear answer :)

/*compile with gcc -o out.x hello.c*/

#include<stdio.h>

int main()
{
    printf("Hello World2\r\n");
    printf("Hello World3\r\n ");

 return 0;
}

Running the following code under Linux give two strings BUT the ending char are differents: the first output ends with 0x0d while the 2nd ends with 0x0d,0x0a.

This is something done by the compiler (GCC) as you can see in the obj file:

Contents of section .rodata:
 400610 01000200 48656c6c 6f20576f 726c6432  ....Hello World2
 400620 0d004865 6c6c6f20 576f726c 64330d0a  ..Hello World3..
 400630 2000                                  .              

So, questions are:

  • Why ?
  • How can i avoid this kind of "optimization"(!?)

Thanks

BFlash
  • 21
  • 4
  • 1
    Why do you want to avoid this optimization? The program’s behavior is completely identical (unless there’s a bug in the compiler); why do you care if the `\n` is in the string literal or inserted when the compiler changes `printf` to `puts`? – Daniel H Jan 03 '18 at 15:58
  • @DanielH: Well...not really the same.. eg.: in embedded world, where the output are send to a terminal (like screen or minicom). With this behavior, some traces can be "hidden" because of the lack of LF – BFlash Jan 03 '18 at 16:11
  • 1
    @BFlash The exact same bytes will be written to `STDOUT` (this is guaranteed because only as-if optimizations are allowed). If you look at [the assembly code](https://godbolt.org/g/6i1pyw), it also replaces the first `printf` with `puts`, which adds a newline at the end. If this doesn’t happen, you have a major bug in your C library. – Daniel H Jan 03 '18 at 16:13
  • Thanks for pointing printf vs puts ! ... it makes sense ! (and you are right for the ... library bug :( ) – BFlash Jan 03 '18 at 16:40
  • I still don't understand why you want to avoid such a legal optimization. Your question lacks some practical motivation and context (maybe you have your own `printf`, but then you should name it differently...) – Basile Starynkevitch Jan 03 '18 at 16:55
  • Please **edit your question** to *provide additional motivation and context*. As you see from the answers, your motivation is important, and I cannot guess it. I can't figure out why in embedded applications you don't want that (legal, and common) optimization to happen. Without motivation, **your question is unclear.** – Basile Starynkevitch Jan 03 '18 at 17:04

2 Answers2

5

Creating formatted output at runtime takes time; the printf call is slow. GCC knows this, so replaces the first function with a call to puts. Since puts automatically adds a \n, GCC needs to remove the \n from the string to compensate.

GCC does this because it considers printf a built-in. Because this has no effect on the bytes output or even on the number of calls to write; I strongly recommend leaving it as-is. If you do want to disable it, you can pass -fno-builtin-printf, but the only effect will be to slow down your code as it tries to format the string unnecessarily.

Daniel H
  • 7,223
  • 2
  • 26
  • 41
3

It is simpler to ask GCC (using GCC7.2 on Linux/Debian/Sid/x86-64) to emit assembler. So I compiled your program bflash.c with

gcc -fverbose-asm -O0 -S bflash.c -o bflash-O0.S

to get it without optimization, and with

gcc -fverbose-asm -O1 -S bflash.c -o bflash-O1.S

to get -O1 optimization. Feel free to repeat the experiment with various other optimization flags.

Even without optimization, the bflash-O0.S contains:

    .section    .rodata
.LC0:
    .string "Hello World2\r"
.LC1:
    .string "Hello World3\r\n "
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp    #
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp  #,
    .cfi_def_cfa_register 6
# bflash.c:5:     printf("Hello World2\r\n");
    leaq    .LC0(%rip), %rdi    #,
    call    puts@PLT    #
# bflash.c:6:     printf("Hello World3\r\n ");
    leaq    .LC1(%rip), %rdi    #,
    movl    $0, %eax    #,
    call    printf@PLT  #
# bflash.c:8:  return 0;
    movl    $0, %eax    #, _4
# bflash.c:9: }
    popq    %rbp    #
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main

As you see, the first printf has been optimized as a puts; and this is permitted by the C11 standard n1570 (as-if rule). BTW, the bflash-01.S contains similar code. Notice that the C11 standard has been specified with current optimization practices in mind (many members of the standardization committees are compiler implementors).

BTW Clang 5, invoked as clang-5.0 -O1 -fverbose-asm -S bflash.c -o bflash-01clang.s, performs the same kind of optimization.

How can i avoid this kind of "optimization"(!?)

Follow Daniel H's answer (and you might compile with -ffreestanding, but I don't recommend that).

Or avoid using printf from the <stdio.h> and implement your own slower printing function. If you implement your own printing function, name it differently (since printf is defined in the C11 standard), and perhaps (if so wanted) write your own GCC plugin to optimize it your way (and that plugin should better be some free software which is GPL compatible, read the GCC runtime library exception).

The C language specification (study n1570) defines a semantics, that is the behavior of your compiled program. It does not require any particular sequence of bytes to appear in the executable (which is probably not even mentioned in the standard). If you need such a property, find a different programming language, and give up all the important optimizations GCC is trying hard to do for you. Optimizations are what is making writing a C compiler difficult (if you want a non-optimizing compiler, use something else than GCC, but accept to lose perhaps a factor of three or more in performance, w.r.t. code compiled with gcc -O2).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • On my system (embedded), puts is from the standard library as printf is rewritten...which lead to this kind of problem. Thanks for your answer ! – BFlash Jan 03 '18 at 16:48
  • Both `puts` and `printf` are standardized in C11. So the compiler is right in performing such an optimization. – Basile Starynkevitch Jan 03 '18 at 16:49